2012-07-11 89 views
10

我曾经想过,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,因此方法调用将静态地(或在通用的解析时间)解析为“new”方法。

所以我的问题是这样的:如何泛型在C#中实现?我不知道CIL字节码,但我知道Java字节码,所以我理解面向对象的CLI语言如何在低级别上工作。随意在该级别解释。另外,我认为C#泛型是以这种方式实现的,因为与Java的类型擦除系统相比,每个人都总是用C#“True Generics”调用通用系统。

+0

任何原因转换为对象在这里'gen ==((object)testVar).GetHashCode()'? – AlwaysAProgrammer 2012-07-11 16:11:21

+0

虽然它不直接回答你的问题,但http://blogs.msdn.com/b/ericlippert/archive/2012/07/10/when-is-a-cast-not-a-cast.aspx有一些好的有关泛型如何投射以及它们在C#中如何相互关联的信息。 – devstruck 2012-07-11 16:11:40

+0

@Yogendra这样做访问Object.GetHashCode()方法而不是“new”方法Test.GetHashCode()。这就是为什么它返回一个不同的值(因为它完全运行一个不同的方法)。 – Carrotman42 2012-07-11 16:19:07

回答

7

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

从埃里克利珀的What's the difference, part one: Generics are not templates,这个问题解释(用来设置略有不同,但教训很好的转化为您的方案):

这说明,在C#泛型是不喜欢的模板在C++中。 你可以将模板看作是一个花式搜索替换 机制。[...]这不是泛型类型的工作方式;通用类型有, 好,通用。我们做超载分辨率一次并烘烤 结果。 [...]我们为通用类型生成的IL已经有 它将要调用的方法。抖动并不是说 “好吧,我碰巧知道,如果我们要求C#编译器现在执行 这个附加信息,那么它会选择一个不同的过载。让我重写生成的代码以忽略C#编译器最初生成的代码 ...“抖动知道 没有任何关于C#的规则。

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

现在,如果你想将基于运行时类型的 参数在运行时重新执行重载决议,我们能做到这一点的您;这就是C#4.0中新的“动态”功能 。只需将“object”替换为“dynamic”,并且当您调用涉及该对象的调用时,我们将在运行时运行过载 解析算法,并动态地吐出代码,调用编译器会选择的方法,如果知道该方法编译时所有的 运行时类型。

+3

阿里埃里克,如果没有你,我们会怎么做。 – Servy 2012-07-11 16:26:02

+0

是的,我说在第一段“我知道这种情况发生的真正原因是......”我的问题是“C#泛型如何实现?”我会阅读你发送的链接,也许这将回答我的问题。 – Carrotman42 2012-07-11 16:30:05

+0

@你的编辑:这不是我想要做的:我来自Java的背景,那里没有整个“新方法”的考验。我认为这很容易出现用户错误,我不打算有意使用它。我碰到它的原因是因为我正在写一个抽象类,我想强制子类来实现GetHashCode,所以我写了“public abstract int GetHashcode();”,没有意识到为了让这个任务一般调用GetHashCode,我不得不实际说“public override abstract int GetHashcode();”,这对我的口味来说非常冗长。 – Carrotman42 2012-07-11 16:36:23