C#Generics是如何实现的?

Car*_*n42 11 c# generics polymorphism overriding

我曾经认为C#中的Generics是这样实现的,当使用新的泛型类型时,在运行时或编译时生成一个新的类/方法/你有什么,类似于C++模板(我从来没有真正调查,我很可能是错的,我很乐意接受纠正.

但在我的编码中,我提出了一个确切的反例:

static class Program {
    static void Main()
    {
        Test testVar = new Test();

        GenericTest<Test> genericTest = new GenericTest<Test>();
        int gen = genericTest.Get(testVar);

        RegularTest regTest = new RegularTest();
        int reg = regTest.Get(testVar);

        if (gen == ((object)testVar).GetHashCode())
        {
            Console.WriteLine("Got Object's hashcode from GenericTest!");
        }
        if (reg == testVar.GetHashCode())
        {
            Console.WriteLine("Got Test's hashcode from RegularTest!");
        }
    }

    class Test
    {
        public new int GetHashCode()
        {
            return 0;
        }
    }

    class GenericTest<T>
    {
        public int Get(T obj)
        {
            return obj.GetHashCode();
        }
    }

    class RegularTest
    {
        public int Get(Test obj)
        {
            return obj.GetHashCode();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这两个控制台线都打印出来.

我知道发生这种情况的实际原因是对Object.GetHashCode()的虚拟调用没有解析为Test.GetHashCode(),因为Test中的方法被标记为new而不是override.因此,我知道如果我在Test.GetHashCode()上使用"覆盖"而不是"新",那么0的返回将多态地覆盖对象中的方法GetHashCode,这不是真的,而是根据我(之前)的理解对于C#泛型来说它不重要,因为T的每个实例都将被Test替换,因此方法调用将静态地(或在通用解析时)被解析为"new"方法.

所以我的问题是:如何在C#中实现泛型?我不知道CIL字节码,但我知道Java字节码,所以我理解面向对象的CLI语言如何在低级别工作.随意在那个级别解释.

顺便说一句,我认为C#泛型是以这种方式实现的,因为与Java的类型擦除系统相比,每个人总是在C#"True Generics"中调用泛型系统.

Ani*_*Ani 7

GenericTest<T>.Get(T),C#编译器已经选择object.GetHashCode应该被调用(虚拟).这将无法GetHashCode在运行时解析为"new" 方法(在方法表中将有自己的插槽,而不是覆盖插槽object.GetHashCode).

来自Eric Lippert的有什么区别,第一部分:泛型不是模板,问题解释了(使用的设置略有不同,但课程很好地适用于您的场景):

这说明C#中的泛型与C++中的模板不同.您可以将模板视为一种花哨的搜索和替换机制.[...]这不是泛型类型的工作方式; 泛型类型是通用的.我们执行一次重载决策并烘焙结果.[...]我们为泛型类型生成的IL已经有了它要调用的方法.抖动并没有说"好吧,我碰巧知道如果我们要求C#编译器立即执行此附加信息,那么它会选择不同的重载.让我重写生成的代码,忽略C#编译器最初生成的代码......"抖动对C#的规则一无所知.

并为您所需的语义解决方法:

现在,如果您确实希望在运行时根据参数的运行时类型重新执行重载决策,我们可以为您执行此操作; 这就是新的"动态"功能在C#4.0中的作用.只需将"object"替换为"dynamic",当你进行涉及该对象的调用时,我们将在运行时运行重载决策算法并动态吐出调用编译器可能选择的方法的代码,让它知道所有运行时编译时的类型.

  • 啊埃里克,没有你我们会做什么. (3认同)