4

我想了解为什么 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 而无需强制转换。但是为什么我不能在不强制转换的情况下检查这两个对象是否相同?他们显然实现了相同的接口,那为什么会出错呢?

4

5 回答 5

11

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

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

于 2013-10-16T19:28:58.433 回答
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 也必须被装箱。它不会框到相同的参考,所以这总是错误的,这看起来很糟糕。

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

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

现在,如果您将 T 约束为引用类型,那么第一个反对意见就会消失,编译器可以合理地选择引用相等。如果这是您的意图,则将 T 限制为引用类型。

于 2013-10-16T20:02:41.053 回答
6

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

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

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 没有值类型约束。

[...]

除非这些条件之一为真,否则会发生绑定时错误。这些规则的显着影响是:

[...]

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

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

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

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

    }
}
于 2013-10-16T20:04:11.810 回答
1

尝试

    if (obj.Equals(obj2))

IA 没有实现任何 == 运算符。

于 2013-10-16T19:33:32.370 回答
0

啊,感谢 Reed Copsey 的说明。

我刚刚还发现您可以像这样将“类”放在 where 子句中。

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

        if (obj == obj2)
        {

        }
    }

现在它是一个引用类型,它可以工作了!:-)

于 2013-10-16T19:36:41.667 回答