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

本文关键字:条件运算符 集合 创建 null 类似于 | 更新日期: 2023-09-27 18:30:39

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(从MoreLinq)会抛出异常。

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

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

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

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

有没有其他更优雅的方法可以做到这一点?

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

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

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

只需使用 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));

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