如何为类似于null条件运算符的集合创建空条件运算符?

时间:2016-01-16 01:12:03

标签: c# collections ienumerable c#-6.0 null-conditional-operator

C#6.0引入了空条件运算符,这是一个很大的胜利。

现在我想拥有一个与它类似的运算符,但是对于空集合。

Where

如果IEnumerable返回空的MinBy,这会爆发,因为如果集合为空,MoreLinq(来自MinByOrDefault)会抛出异常。

在C#6.0之前,可能会通过添加另一个扩展方法 .Where(...)?.MinBy(...)来解决这个问题。

我想像这样重写:.Where。但这不起作用,因为null会返回空集合而不是.NullIfEmpty()

现在可以通过为IEnumerable引入.Where(...).NullIfEmpty()?.MinBy()扩展方法来解决这个问题。到达null

最终这看起来很尴尬,因为返回空集合总是优先于返回{{1}}。

还有其他更优雅的方法吗?

2 个答案:

答案 0 :(得分:2)

恕我直言,"最优雅的"解决方案是重新编写MinBy以使其进入MinByOrDefault

public static TSource MinByOrDefault<TSource, TKey>(this IEnumerable<TSource> source,
    Func<TSource, TKey> selector)
{
    return source.MinByOrDefault(selector, Comparer<TKey>.Default);
}

public static TSource MinByOrDefault<TSource, TKey>(this IEnumerable<TSource> source,
    Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
    if (source == null) throw new ArgumentNullException("source");
    if (selector == null) throw new ArgumentNullException("selector");
    if (comparer == null) throw new ArgumentNullException("comparer");
    using (var sourceIterator = source.GetEnumerator())
    {
        if (!sourceIterator.MoveNext())
        {
            return default(TSource); //This is the only line changed.
        }
        var min = sourceIterator.Current;
        var minKey = selector(min);
        while (sourceIterator.MoveNext())
        {
            var candidate = sourceIterator.Current;
            var candidateProjected = selector(candidate);
            if (comparer.Compare(candidateProjected, minKey) < 0)
            {
                min = candidate;
                minKey = candidateProjected;
            }
        }
        return min;
    }
}

我认为不需要特殊的操作员。

答案 1 :(得分:2)

如果它是空的,只需使用DefaultIfEmtpy来定义要放入序列的默认项目:

Region smallestFittingFreeRegion = FreeRegions
    .Where(region => region.Rect.W >= width && region.Rect.H >= height) 
    .DefaultIfEmpty()               
    .MinBy(region => (region.Rect.W - width) * (region.Rect.H - height));

当然,如果您想提供自己的默认值,以便在类型的默认值不是您想要的情况下使用,您可以使用接受第二个参数的重载。