2

C# 6.0 引入了空条件运算符,大获全胜。

现在我想要一个行为类似于它的运算符,但用于空集合。

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

现在如果Where返回一个空IEnumerable,这会爆炸,因为如果集合为空, MinBy(from MoreLinq) 会引发异常。

在 C# 6.0 之前,这可能会通过添加另一个扩展方法 MinByOrDefault来解决。

我想像这样重写它:.Where(...)?.MinBy(...). 但这不起作用,因为.Where返回一个空集合而不是null.

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

最终这似乎很尴尬,因为返回空集合总是比返回null.

有没有其他更优雅的方式来做到这一点?

4

2 回答 2

3

恕我直言,“最优雅”的解决方案是重写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;
    }
}

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

于 2016-01-16T03:53:26.197 回答
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));

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

于 2016-01-16T03:59:40.690 回答