我想了解为什么C#语言决定将此测试表达式作为错误。
interface IA { }
interface IB { }
class Foo : IA, IB { }
class Program
{
static void testFunction<T>(T obj) where T : IA, IB
{
IA obj2 = obj;
if (obj == obj2) //ERROR
{
}
}
static void Main(string[] args)
{
Foo myFoo = new Foo();
testFunction(myFoo);
Console.ReadLine();
}
}
在testFunction中,我可以创建一个名为obj2的对象,并在不强制转换的情况下将其隐式设置为obj。但是为什么我不能检查这两个对象,看看它们是否相同,没有铸造它?他们显然实现了相同的接口,为什么它是一个错误?
答案 0 :(得分:11)
您可以使用Object.ReferenceEquals
或Object.Equals
来查看它们是否是同一个对象。
但是,由于您的约束(IA
和IB
接口)不强制该类型必然是引用类型,因此无法保证可以使用相等运算符。
答案 1 :(得分:11)
假设您使用实现IA的值类型X构造T.
什么是
static void testFunction<T>(T obj) where T : IA
{
IA obj2 = obj;
if (obj == obj2) //ERROR
在被称为testFunction<X>(new X(whatever))
?
T是X,X实现IA,因此隐式转换框obj到obj2。
等于运算符现在将值类型X与编译时类型IA的盒装副本进行比较。运行时类型是编译器不关心的盒装X;该信息被忽略。
它应该使用什么比较语义?
它不能使用引用比较语义,因为这意味着obj也必须加框。它不会包装到相同的引用,所以这总是是假的,这看起来很糟糕。
它不能使用 value 比较语义,因为编译器没有依据它应该使用哪种值语义!在编译时,它不知道将来为T选择的类型是否会提供过载的==运算符,即使它确实如此,该运算符也不可能将IA作为其操作数之一。
编译器可以合理选择没有相等的语义,因此这是非法的。
现在,如果您将T约束为引用类型,则第一个异议消失,编译器可以合理地选择引用相等。如果这是你的意图,那么将T约束为参考类型。
答案 2 :(得分:6)
要扩展一点Reed的答案(肯定是正确的):
请注意,以下代码在编译时会导致相同的错误:
Guid g = Guid.NewGuid(); // a value type
object o = g;
if (o == g) // ERROR
{
}
C#语言规范说(§7.10.6):
预定义的引用类型相等运算符是:
bool operator ==(object x, object y);
bool operator !=(object x, object y);
[...]
预定义的引用类型相等运算符需要以下之一:
- 两个操作数都是已知为引用类型或文字
null
的类型的值。 此外,从操作数的类型到另一个操作数的类型,存在显式引用转换(第6.2.4节)。- 一个操作数是
T
类型的值,其中T
是类型参数,另一个操作数是文字null
。此外,T没有值类型约束。[...]
除非其中一个条件成立,否则会发生绑定时错误。这些规则的显着含义是:
[...]
- 预定义的引用类型相等运算符不允许比较值类型操作数。因此,除非struct类型声明自己的相等运算符,否则无法比较该结构类型的值。
- 预定义的引用类型相等运算符永远不会导致其操作数发生装箱操作。执行此类装箱操作将毫无意义,因为对新分配的盒装实例的引用必然会与所有其他引用不同。
现在,在您的代码示例中,您不会将T
约束为引用类型,因此您会收到编译时错误。但是,您可以通过声明T
必须是引用类型来修复您的示例:
static void testFunction<T>(T obj) where T : class, IA, IB
{
IA obj2 = obj;
if (obj == obj2) // compiles fine
{
}
}
答案 3 :(得分:1)
尝试
if (obj.Equals(obj2))
IA没有实现任何==运算符。
答案 4 :(得分:0)
啊,感谢Reed Copsey的那张纸条。
我还发现你可以把“class”放在像这样的where子句中。
static void testFunction<T>(T obj) where T : class, IA, IB
{
IA obj2 = obj;
if (obj == obj2)
{
}
}
现在它是一个引用类型,它的工作原理! : - )