泛型类型的等于运算符和默认值是 C# 中两个方便的功能。但我们不能轻易无缝地使用它们。例如,我希望下面的代码可以编译,
public static bool EqualsDefault<T>(T subject){
return subject == default(T);
}
不幸的是,它会失败,尽管有一个违反直觉的替代方案,
public static bool EqualsDefault<T>(T subject){
return object.Equals(subject, default(T));
}
所以我的问题是为什么 C# 不允许第一个代码片段?
最佳答案
它不起作用的原因是内置引用相等运算符不能应用于值类型。
让我们退后一步,注意 System.Object事实上,并没有定义相等运算符 ==
. C# 语言定义了一个带有签名的内置引用相等运算符(请参阅 C# 5 规范的第 7.6.10 节):
bool operator ==(object x, object y);
但是,关于何时可以应用它有两个规则:
The predefined reference type equality operators require one of the following:
- Both operands are a value of a type known to be a reference-type or the literal null. Furthermore, an explicit reference conversion (§6.2.4) exists from the type of either operand to the type of the other operand.
- One operand is a value of type T where T is a type-parameter and the other operand is the literal null. Furthermore T does not have the value type constraint.
然后规范指出,这意味着将运算符应用于两个值类型是错误的,除非该类型明确定义了相等运算符。由于您没有约束,因此允许值类型并且两个操作数都不为空。因此,无法应用内置的相等运算符并产生错误。
要解决这个问题,您可以约束 T
是一个引用类型:
public static bool EqualsDefault<T>(T subject) where T : class {
return subject == default(T);
}
但是您需要注意,以上始终是引用比较。编译器只会调用 ==
编译时最具体适用类型的运算符,在本例中为 object
.
更好的选择是使用 EqualityComparer<T>.Default
防止值类型装箱:
public static bool EqualsDefault<T>(T subject) {
return EqualityComparer<T>.Default.Equals(subject, default(T));
}
我想您可能会问,为什么 C# 没有设计为具有默认的相等运算符,该运算符无需装箱即可应用于值类型。我不知道完整的原因,但我怀疑确定在哪些情况下调用哪些方法可能比现在更令人困惑。我认为如果在普通方法中调用重载运算符但在泛型方法中使用另一种机制,那将是不可取的。尽管您可以争辩说现在可以通过引用类型发生这种情况。
关于c# - 是否有任何令人信服的理由不能在 C# 中对默认值 (T) 使用等于运算符 (==),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29596681/