2016-01-16 121 views
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