C# · 12月 27, 2021

C#泛型如何实现?

我以为在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()中使用了“override”而不是“new”,那么返回0将会多次覆盖对象中的GetHashCode方法,这不是真的,但是根据我的(上一个)理解的C#泛型,因为T的每个实例都将被Test替换,因此方法调用将具有静态(或者在一般的解析时间)被解析为“新”方法.

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

除此之外,我认为C#泛型是这样实现的,因为与Java的类型擦除系统相比,每个人都总是调用C#“True Generic”中的通用系统.

解决方法 在GenericTest> .Get(T)中,C#编译器已经选择了该对象.GetHashCode应被调用(虚拟).没有办法可以解析为运行时的“新”GetHashCode方法(它将在方法表中具有自己的插槽,而不是覆盖object.GetHashCode的插槽).

来自Eric Lippert的What’s the difference,part one: Generics are not templates,问题解释(使用的设置略有不同,但是课程可以很好地适应您的场景):

This illustrates that generics in C# are not like templates in C++.
You can think of templates as a fancy-pants search-and-replace
mechanism.[…] That’s not how generic types work; generic types are,
well,generic. We do the overload resolution once and bake in the
result. […] The IL we’ve generated for the generic type already has
the method its going to call picked out. The jitter does not say
“well,I happen to kNow that if we asked the C# compiler to execute
right Now with this additional information then it would have picked a
different overload. Let me rewrite the generated code to ignore the
code that the C# compiler originally generated…” The jitter kNows
nothing about the rules of C#.

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

Now,if you do want overload resolution to be re-executed at runtime based on the runtime types of the arguments,we can do that for you; that’s what the new “dynamic” feature does in C# 4.0. Just replace “object” with “dynamic” and when you make a call involving that object,we’ll run the overload resolution algorithm at runtime and dynamically spit code that calls the method that the compiler would have picked,had it kNown all the runtime types at compile time.