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();
    }
}
在testFunction中,我可以创建一个名为obj2的对象,并在不强制转换的情况下将其隐式设置为obj.但是为什么我不能检查这两个对象,看看它们是否相同,没有铸造它?他们显然实现了相同的接口,为什么它是一个错误?
Ree*_*sey 11
您可以使用Object.ReferenceEquals或检查它们是否是同一个对象Object.Equals.
但是,由于您的约束(IA和IB接口)不强制该类型必然是引用类型,因此无法保证可以使用相等运算符.
Eri*_*ert 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约束为参考类型.
为了扩展里德的答案(这当然是正确的):
请注意,以下代码在编译时导致相同的错误:
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节).- 一个操作数是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
    {
    }
}