仅当第二个TSource不为空时才使用联合集合

时间:2014-12-16 19:45:35

标签: c# .net linq ienumerable

我有以下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。

4 个答案:

答案 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;
}

或者在你的情况下最有意义的任何特定逻辑(上面将允许可枚举为n​​ull ...例如,你可能只想允许第二个。)

有些吝啬鬼可能会觉得OP无法根据自己的需要调整这个想法。我认为他可能没有任何问题,如果没有变量声明我就无法准确地显示出它的样子。但它会像 bit 这样:

public static List<Type> SafeUnion(this Group group1, Group group2)
{
    return (group2 != null ?
            group1.GetTypes().Union(group2.GetTypes()) : group1.GetTypes();
}

当然,Group类型需要替换为实际上这些变量的类型。此示例也不允许group1为空。如果需要,可能读者可以自己想出改变。