为什么在空引用列表上调用Min()不会抛出?

时间:2015-12-29 09:17:04

标签: c# ienumerable value-type reference-type

请考虑以下事项:

var emptyValueList = new List<int>(); //any value type (long, float, any struct ...)
var minimum = emptyValueList.Min();

这将抛出InvalidOperationException序列不包含元素)。

现在让我们尝试使用引用类型:

var emptyReferenceList = new List<object>(); //any ref type will do such as object ...
var minimum = emptyReferenceList.Min();

这不会抛出并返回null。好像第default个操作符被调用第二个案例而不是第一个案例。它适用于可空类型(例如int?,即使它们是值类型)。

我想知道为什么会这样,如果背后有一个具体的推理?

2 个答案:

答案 0 :(得分:6)

这是一个设计决定,我们将不得不向BCL的作者询问。

Min extension method有各种重载。对于允许null的类型,我相信Min会在搜索最小值时跳过所有null值。这适用于两种引用类型和type Nullable<>(在您的示例Nullable<int>中),它不是引用类型,但允许nullMin方法决定忽略)。

因此,对于不可为空的结构,Min的作者可能认为它会是危险的&#34;返回default(TSource),因为在其他情况下(通常是数字0),这可能是有意义的(即非空)最小值,因此输出可能会被误解。

对于允许null的类型,由于作者选择跳过源自null的值,因此可以安全地假设仅当序列除了{之外的任何内容时才返回null {1}}值(包括en empty source的情况)。

请注意,对于可空类型或引用类型,在标准null下,IComparer<>值小于任何非空值。对于排序算法(如null使用的算法), order 必须是完全且可传递的。因此,我们得到:

List<>.Sort

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" 对于真正的引用类型的工作方式相同,例如System.VersionMin类型):

class

答案 1 :(得分:0)

您也可以考虑使用扩展程序:

using System.Linq;
public static class EnumerableExtensions {

    public static X MinOrDefault<T, X>(this IEnumerable<T> that, Func<T, X> minXp, X defaultValue) {
        if (that.Any())
            return that.Min(minXp);
        else
            return defaultValue;
    }
}

...

var emptyValueList = new List<int>();
var minimum = emptyValueList.MinOrDefault(e => e, 0);

var emptyEmployeeList = new List<Employee>();
var minimumSalary = emptyEmployeeList.MinOrDefault(e => e.Salary, 0);