泛型类型的相等运算符和默认值是C#中的两个便利功能.但我们无法轻易地无缝使用它们.例如,我希望以下代码可以编译,
public static bool EqualsDefault<T>(T subject){
return subject == default(T);
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,它会失败,尽管有一个反直觉的选择,
public static bool EqualsDefault<T>(T subject){
return object.Equals(subject, default(T));
}
Run Code Online (Sandbox Code Playgroud)
所以我的问题是为什么C#不允许第一个代码片段?
它不起作用的原因是内置引用相等运算符不能应用于值类型.
让我们退后一步,注意System.Object实际上并没有定义一个相等运算符==.C#语言定义了带签名的内置引用相等运算符(参见C#5规范的第7.6.10节):
bool operator ==(object x, object y);
Run Code Online (Sandbox Code Playgroud)
但是,有两个规则可以应用:
预定义的引用类型相等运算符需要以下之一:
- 两个操作数都是已知为引用类型或文字null的类型的值.此外,从操作数的类型到另一个操作数的类型存在显式引用转换(第6.2.4节).
- 一个操作数是类型T的值,其中T是类型参数,另一个操作数是文字null.此外,T没有值类型约束.
该规范随后指出,这意味着将运算符应用于两个值类型是错误的,除非该类型显式定义了相等运算符.由于没有约束,因此允许值类型,并且两个操作数都不为null.因此,无法应用内置的相等运算符并产生错误.
要解决此问题,您可以约束T为引用类型:
public static bool EqualsDefault<T>(T subject) where T : class {
return subject == default(T);
}
Run Code Online (Sandbox Code Playgroud)
但是,您需要注意以上内容始终是参考比较.编译器将仅==在编译时调用最具体的适用类型的运算符,在本例中为object.
更好的选择是使用EqualityComparer<T>.Default以防止值类型的装箱:
public static bool EqualsDefault<T>(T subject) {
return EqualityComparer<T>.Default.Equals(subject, default(T));
}
Run Code Online (Sandbox Code Playgroud)
我想你可以问为什么C#没有设计成一个默认的相等运算符,可以应用于没有装箱的值类型.我不知道完整的原因,但我怀疑现在确定在哪种情况下调用哪种方法可能会比现在更令人困惑.我认为如果在普通方法中调用重载运算符,但在泛型方法中使用另一种机制则不合需要.虽然你可以说现在可以用引用类型发生.
| 归档时间: |
|
| 查看次数: |
90 次 |
| 最近记录: |