为什么在空的引用列表上调用Min()不会引发
本文关键字:Min 调用 引用 列表 为什么 | 更新日期: 2023-09-27 18:28:30
考虑以下内容:
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
运算符,但第一种情况没有调用。它也适用于可为null的类型(例如int?
,即使它们是值类型)。
我想知道为什么会这样,这背后是否有具体的原因?
这是我们必须询问BCL作者的设计决策。
Min
扩展方法存在各种重载。对于允许null
的类型,我相信Min
在搜索最小值时会跳过所有null
值。这适用于引用类型和类型Nullable<>
(在您的示例中为Nullable<int>
),后者不是引用类型,但允许null
(Min
方法决定忽略)。
因此,对于不可为null的结构,Min
的作者可能认为返回default(TSource)
是"危险的",因为在其他情况下(通常是数字0
),这可能是一个有意义的全(即非null)最小值,因此输出可能会被误解。
对于允许null
的类型,因为作者选择跳过从源中产生的null
值,所以可以放心地假设,只有当序列只包含null
值时(包括空源的情况),才返回null
。
请注意,在可为null的类型或引用类型的标准IComparer<>
下,null
值小于任何非null值。对于排序算法(如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"
Min
对真正的引用类型的工作方式相同,例如System.Version
(它是class
类型):
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());
您也可以考虑使用扩展:
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);