我有以下linq声明:
List<Type> allTypes = group.GetTypes().Union(group2.GetTypes()).ToList();
可能有一种情况,当group2为null时会抛出NullReferenceException
解决此问题的一种方法是先执行空检查,例如:
if (group2 != null)
{
List<Type> allTypes = group.GetTypes().Union(group2.GetTypes()).ToList();
}
else
{
List<Type> allTypes = group.GetTypes();
}
但问题是我有不同类型的许多类似的赋值,并且不希望以这种方式为每个类型执行if语句,但我宁愿将null check放在一行中,例如:< / p>
List<Type> allTypes = group.GetTypes().Union((if group2 != null)group2.GetTypes()).ToList();
但不确定如何使用linq。
答案 0 :(得分:4)
你在这里遇到的问题不是TSource为空;它想要获取源的对象是null(group2
)。
您可以随时使用Enumerable.Empty
来保存神奇的单线。
List<Type> allTypes = group.GetTypes().Union(group2 != null ? group2.GetTypes() : Enumerable.Empty<Type>()).ToList();
或者您可以使用可重复使用的Union
重载:
public static IEnumerable<T> Union<T>(this IEnumerable<IEnumerable<T>> source)
{
var set = new HashSet<T>();
foreach (var s in source)
{
foreach (var item in s)
{
if (set.Add(item))
yield return item;
}
}
}
然后你的代码变成:
var allTypes = new [] { group, group2 }.Where(x => x != null).Select(x => x.GetTypes()).Union().ToList();
这种方法的优点是你可以有两个以上的序列组成一个联合。
答案 1 :(得分:3)
这里你需要的是一种获取可以支持null参数的组类型的方法,因为你的不支持。这是一个非常简单的写法:
public static IEnumerable<Type> MyGetTypes(Group group)
{
if(group == null)
return Enumerable.Empty<Type>();
else
return group.GetTypes();
}
(如果需要,可以将其作为扩展方法)
您现在可以将原始代码编写为:
var allTypes = MyGetTypes(group).Union(MyGetTypes(group2)).ToList();
如果我们想要的话,我们也可以概括这个而不是使这个方法如此具体。
public static TResult Use<TSource, TResult>(TSource source,
Func<TSource, TResult> selector,
TResult defaultValue = default(TResult))
{
if (source == null)
return defaultValue;
else
return selector(source);
}
这将让我们写一下:
var allTypes = group.GetTypes()
.Union(group2.Use(g => g.GetTypes(), Enumerable.Empty<Type>()))
.ToList();
当C#6.0发布并且我们可以访问?.
运算符时,您也可以像这样编写代码:
var allTypes = group.GetTypes()
.Union(group2?.GetTypes() ?? Enumerable.Empty<Type>())
.ToList();
这允许空组传播到类型的空集合,而不是抛出,然后允许将空值替换为Union
将支持的空集合。这个运算符或多或少是我们的Use
方法的内置版本,但它允许我们避免使用lambda,使其明显更简洁。
答案 2 :(得分:2)
我发现在一行中进行空检查的最好的是ternary operator。
List<Type> allTypes = group2 == null ?
group.GetTypes()
: group.GetTypes().Union(group2.GetTypes()).ToList();
答案 3 :(得分:2)
对于这种情况,我通常会创建一个新的扩展方法。 E.g:
public static IEnumerable<T> SafeUnion<T>(
this IEnumerable<T> source1, IEnumerable<T> source2)
{
return source1 != null ?
(source2 != null ? source1.Union(source2) : source1) : source2;
}
或者在你的情况下最有意义的任何特定逻辑(上面将允许可枚举为null ...例如,你可能只想允许第二个。)
有些吝啬鬼可能会觉得OP无法根据自己的需要调整这个想法。我认为他可能没有任何问题,如果没有变量声明我就无法准确地显示出它的样子。但它会像 bit 这样:
public static List<Type> SafeUnion(this Group group1, Group group2)
{
return (group2 != null ?
group1.GetTypes().Union(group2.GetTypes()) : group1.GetTypes();
}
当然,Group
类型需要替换为实际上这些变量的类型。此示例也不允许group1
为空。如果需要,可能读者可以自己想出改变。