Consider the following:
var emptyValueList = new List<int>(); //any value type (long, float, any struct ...)
var minimum = emptyValueList.Min();
This will throw an InvalidOperationException
(Sequence contains no elements).
Now let's try with a reference type:
var emptyReferenceList = new List<object>(); //any ref type will do such as object ...
var minimum = emptyReferenceList.Min();
This does not throw and returns null
. It's as if the default
operator was called for the second case but not for the first. It works as well for nullable types (e.g. int?
, even though they're value types).
I was wondering why that was the case and if there was a specific reasoning behind this?
That is a design decision that we will have to ask the authors of the BCL about.
There are various overloads of the Min
extension method. For types that allow null
, I believe Min
skips all null
values when searching for the minimum. This goes for both reference types and the type Nullable<>
(in your example Nullable<int>
) which is not a reference type, but which allows null
(that the Min
methods decide to disregard).
So with non-nullable structs, the authors of Min
probably thought it would be "dangerous" to return default(TSource)
since that could be a meaningfull (i.e. non-null) minimum in other cases (often the number 0
), and hence the output could be misunderstood.
With types that allow null
, because the authors chose to skip null
values yielded from the source, one can safely assume that null
is returned only if the sequence contains nothing but null
values (including the case of en empty source).
Note that under the standard IComparer<>
for nullable types or reference types, the null
value is less than any non-null value. For sorting algorithms (like the one used by List<>.Sort
) the order must be total and transitive. We therefore get:
Console.WriteLine(
Comparer<int?>.Default.Compare(null, 7)
); // "-1"
Console.WriteLine(
Nullable.Compare((int?)null, 7)
); // "-1"
Console.WriteLine(
new List<int?> { 9, null, 7, 13, }
.OrderBy(ni => ni).First()
); // ""
// 'Min' is special in that it disregards 'null' values
Console.WriteLine(
new List<int?> { 9, null, 7, 13, }
.Min()
); // "7"
And Min
works the same way for true reference types, for example System.Version
(which is a class
type):
var li = new List<Version> { new Version("9.0"), null, new Version("7.0"), new Version("13.0"), };
Console.WriteLine(li.OrderBy(ve => ve).First());
Console.WriteLine(li.Min());