为什么这个测试表达式出错?

时间:2013-10-16 19:24:40

标签: c# generics interface polymorphism

我想了解为什么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。但是为什么我不能检查这两个对象,看看它们是否相同,没有铸造它?他们显然实现了相同的接口,为什么它是一个错误?

5 个答案:

答案 0 :(得分:11)

您可以使用Object.ReferenceEqualsObject.Equals来查看它们是否是同一个对象。

但是,由于您的约束(IAIB接口)不强制该类型必然是引用类型,因此无法保证可以使用相等运算符。

答案 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)
        {

        }
    }

现在它是一个引用类型,它的工作原理! : - )