C# 6.0 introduced null-conditional operator, a big win.
Now I would like to have an operator which behaves similarly to it, but for empty collections.
Region smallestFittingFreeRegion = FreeRegions
.Where(region => region.Rect.W >= width && region.Rect.H >= height)
.MinBy(region => (region.Rect.W - width) * (region.Rect.H - height));
Now this blows up if Where
returns an empty IEnumerable
, because MinBy
(from MoreLinq
) throws an exception if collection is empty.
Before C# 6.0 this would probably be solved by adding another extension method MinByOrDefault
.
I would like to re-write it like this: .Where(...)?.MinBy(...)
. But this doesn't work because .Where
returns an empty collection instead of null
.
Now this could be solved by introducing a .NullIfEmpty()
extension method for IEnumerable
. Arriving at .Where(...).NullIfEmpty()?.MinBy()
.
Ultimately this seems awkward because returning empty collection has always been preferable to returning a null
.
Is there an other more-elegant way to do this?
IMHO, the "most elegent" solution is to re-write MinBy
to make it in to a 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;
}
}
I don't see much need for a special operator.