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

C# 如何为集合创建与null条件运算符类似的空条件运算符?,c#,collections,ienumerable,c#-6.0,null-conditional-operator,C#,Collections,Ienumerable,C# 6.0,Null Conditional Operator,C#6.0引入了空条件运算符,这是一个巨大的胜利 现在我希望有一个与之行为类似的操作符,但用于空集合 Region smallestFittingFreeRegion = FreeRegions .Where(region => region.Rect.W >= width && region.Rect.H >= height) .MinBy(region => (region

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));
现在,如果
返回一个空的
IEnumerable
,则会出现此问题,因为
MinBy
(from
MoreLinq
)在集合为空时抛出异常

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

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

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

最终这看起来很尴尬,因为返回空集合总是比返回
null
更好

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

我认为,“最优雅”的解决方案是重新写入到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;
    }
}
public static TSource MinByOrDefault(此IEnumerable源,
Func选择器)
{
返回source.MinByOrDefault(选择器、比较器、默认值);
}
公共静态TSource MinByOrDefault(此IEnumerable源,
Func选择器,IComparer(比较器)
{
如果(source==null)抛出新的ArgumentNullException(“source”);
如果(选择器==null)抛出新的ArgumentNullException(“选择器”);
如果(comparer==null)抛出新的ArgumentNullException(“comparer”);
使用(var sourceIterator=source.GetEnumerator())
{
如果(!sourceIterator.MoveNext())
{
返回默认值(TSource);//这是唯一更改的行。
}
var min=sourceIterator.Current;
var minKey=选择器(最小值);
while(sourceIterator.MoveNext())
{
var candidate=sourceIterator.Current;
var candidateProjected=选择器(候选);
if(comparer.Compare(candidateProjected,minKey)<0)
{
min=候选人;
minKey=候选项目;
}
}
返回最小值;
}
}

我认为不太需要特殊运算符。

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

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

我假设您不会在
if
语句中使用
Any
调用“优雅”?您提出了两个简单的解决方案,它们紧凑且易于理解。你想要多优雅?你试过这个吗
.DefaultIfEmpty().MinBy(region=>(region?.Rect?.W-width)*(region?.Rect?.H-height))
D Stanley,stackoverflow用户的聪明才智经常让我惊讶。空枚举是完全有效的枚举,不需要任何特殊处理,与null不同,null的含义不明确,不允许任何有效操作。您的问题实际上是
MinBy
方法对非空输入的要求。此外,如果没有匹配的元素,您希望
MinBy
返回什么?如果region是引用类型,这将不起作用,因为这将导致.MinBy.@jbeur中出现空指针异常。正如我所说的,您可以传递任何您想要使用的默认值,如果您想要这样做的话。