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(从MoreLinq)抛出一个异常,如果集合为空。

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

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

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

最终这似乎很尴尬,因为返回空收集一直比返回null更可取。

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

+0

我假设你不会在'if'语句中使用'Any'调用“elegant”? –

+2

您提出了两种简洁易懂的解决方案。你想要多优雅? –

+0

你试过这个吗? '.DefaultIfEmpty()。MinBy(region =>(region?.Rect?.W - width)*(region?.Rect?.H - height))' – Enigmativity

回答

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

我没有看到一个特殊的运算符太大必要。

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)); 

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

+0

如果region是引用类型,则这不起作用,因为这会导致.MinBy中的空指针异常。 – JBeurer

+0

@JBeurer正如我所说,你可以传递任何你想使用的默认值,如果这是你想要做的。 – Servy