繁体   English   中英

C#泛型是如何实现的?

[英]How are C# Generics implemented?

我曾认为 C# 中的泛型是这样实现的,即在运行时或编译时生成一个新的类/方法/你有什么,当使用新的泛型类型时,类似于 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();
        }
    }
}

这两个控制台行都打印出来。

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

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

顺便说一句,我认为 C# 泛型是以这种方式实现的,因为与 Java 的类型擦除系统相比,每个人都将 C# 中的泛型系统称为“真正的泛型”。

GenericTest<T>.Get(T) ,C# 编译器已经选择了那个对象object.GetHashCode应该被调用(实际上)。 这不可能在运行时解析为“新的” GetHashCode方法(它将在方法表中有自己的插槽,而不是覆盖object.GetHashCode的插槽)。

来自 Eric Lippert 的What's the difference, part 1: 泛型不是模板,解释了问题(使用的设置略有不同,但课程可以很好地转化为您的场景):

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

以及您所需语义的解决方法:

现在,如果您确实希望根据参数的运行时类型在运行时重新执行重载解析,我们可以为您完成; 这就是 C# 4.0 中新的“动态”功能所做的。 只需将“对象”替换为“动态”,当您进行涉及该对象的调用时,我们将在运行时运行重载解析算法并动态生成调用编译器会选择的方法的代码,如果它知道所有运行时编译时的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM