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

Ass*_*ast 4 c# generics polymorphism interface

我想了解为什么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();
    }
}
Run Code Online (Sandbox Code Playgroud)

在testFunction中,我可以创建一个名为obj2的对象,并在不强制转换的情况下将其隐式设置为obj.但是为什么我不能检查这两个对象,看看它们是否相同,没有铸造它?他们显然实现了相同的接口,为什么它是一个错误?

Ree*_*sey 11

您可以使用Object.ReferenceEquals或检查它们是否是同一个对象Object.Equals.

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


Eri*_*ert 11

假设您使用实现IA的值类型X构造T.

是什么

static void testFunction<T>(T obj) where T : IA
{
    IA obj2 = obj;
    if (obj == obj2) //ERROR
Run Code Online (Sandbox Code Playgroud)

什么时候叫做testFunction<X>(new X(whatever))

T是X,X实现IA,因此隐式转换框obj到obj2.

等于运算符现在将值类型X与编译时类型IA的盒装副本进行比较.运行时类型是编译器不关心的盒装X; 该信息被忽略.

它应该使用什么比较语义?

它不能使用引用比较语义,因为这意味着obj也必须加框.它不会包含相同的引用,所以这总是错误的,这看起来很糟糕.

它不能使用比较语义,因为编译器没有依据它应该使用哪种值语义!在编译时,它不知道将来为T选择的类型是否会提供过载的==运算符,即使它确实如此,该运算符也不可能将IA作为其操作数之一.

编译器可以合理地选择没有相等的语义,因此这是非法的.

现在,如果您将T约束为引用类型,则第一个异议消失,编译器可以合理地选择引用相等.如果这是你的意图,那么将T约束为参考类型.


afr*_*hke 6

为了扩展里德的答案(这当然是正确的):

请注意,以下代码在编译时导致相同的错误:

Guid g = Guid.NewGuid(); // a value type
object o = g;

if (o == g) // ERROR
{
}
Run Code Online (Sandbox Code Playgroud)

C#语言规范说(§7.10.6):

预定义的引用类型相等运算符是:

  • bool operator ==(object x, object y);
  • bool operator !=(object x, object y);

[...]

预定义的引用类型相等运算符需要以下之一:

  • 两个操作数都是已知为引用类型或文字的类型的值null.此外,从操作数的类型到另一个操作数的类型存在显式引用转换(第6.2.4节).
  • 一个操作数是type的值,T其中T是一个类型参数,另一个操作数是文字null.此外,T没有值类型约束.

[...]

除非其中一个条件成立,否则会发生绑定时错误.这些规则的显着含义是:

[...]

  • 预定义的引用类型相等运算符不允许比较值类型操作数.因此,除非结构类型声明其自己的相等运算符,否则无法比较该结构类型的值.
  • 预定义的引用类型相等运算符永远不会导致其操作数发生装箱操作.执行这样的装箱操作是没有意义的,因为对新分配的盒装实例的引用必然不同于所有其他引用.

现在,在您的代码示例中,您不限制T为引用类型,因此您会收到编译时错误.您的样本可以通过声明T必须是引用类型来修复:

static void testFunction<T>(T obj) where T : class, IA, IB
{
    IA obj2 = obj;

    if (obj == obj2) // compiles fine
    {

    }
}
Run Code Online (Sandbox Code Playgroud)