C# 中的多态、重载和泛型

Posted

技术标签:

【中文标题】C# 中的多态、重载和泛型【英文标题】:Polymorphism, overloads and generics in C# 【发布时间】:2012-01-20 11:42:57 【问题描述】:
class Poly
    
    public static void WriteVal(int i)  System.Console.Write("0\n", i); 
    public static void WriteVal(string s)  System.Console.Write("0\n", s); 
    

class GenWriter<T>
    
        public static void Write(T x)  Poly.WriteVal(x); 
    

为什么在 C# 中不能接受无辜的(针对 C++ 程序员的)方法 Write?

可以看到编译器尝试将参数类型T匹配到具体的重载之前实例化:

错误 3 'TestGenericPolyMorph.Poly.WriteVal(int)' 的最佳重载方法匹配有一些无效参数

当然。目的不是像上面那样使用静态方法,目的是创建一个具有多态行为的包装器。 注意:我使用的是 VS 2010。

请注意,所有需要的信息都在编译时可用。再说一遍:问题是验证是在模板实例化之前执行的。

讨论后补充:

好吧,可能是我没有正确强调这一点。 问题不仅在于泛型和模板之间的区别,还在于解决以下问题:给定一组针对不同类型的重载,我想生成一组包装类,为这些类型提供虚拟方法(多态性)。 在运行时解析虚拟方法的代价是最小的并且不会影响性能。这就是 C++ 模板派上用场的地方。显然,dynamic 的运行时类型解析的开销是完全不同的。 因此,问题是是否可以在不复制代码且不支付性能损失的情况下将现有重载转换为多态(例如,我不确定 dynamic 与“switch”试图强制转换,除了更好的语法)。

到目前为止,我看到的解决方案之一是生成/发出代码(原文如此!),即自动执行此操作而不是剪切和粘贴。

因此,我们不使用 C++ 模板处理,而是简单地手动进行或重新发明宏/模板处理器。

还有更好的吗?

【问题讨论】:

"问题是验证是在模板实例化之前进行的。" - 是的,这是真的。你不能在 C# 中这样做,因为编译器在编译时不知道 x 的类型。 What are the differences between Generics in C# and Java... and Templates in C++?的可能重复 【参考方案1】:

简答:

C# 泛型不是 C++ 模板;尽管它们的语法相似,但它们却截然不同。模板是在编译时构建的,每次实例化一次,模板化代码必须是正确的只有实际提供的模板参数。模板在每次实例化时执行一次重载解析和类型分析等任务;它们基本上是源代码文本上的智能“搜索和替换”机制。

C# 泛型是真正的泛型类型;对于任何可能的类型参数,它们必须是正确的。通用代码被分析一次,重载解析被完成一次,等等。

长答案:这是

的副本

What are the differences between Generics in C# and Java... and Templates in C++?

详情请参阅那里的长答案。

另请参阅我关于该主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx

【讨论】:

【参考方案2】:

你为什么不能简单地写:

public static void Write<T>(T x)  System.Console.Write("0\n", x); 

【讨论】:

因为在这种情况下(可能不是这种情况)已经存在多态性:为所有相关类型定义了 x.ToString()。我试图用提供虚拟方法的类来包装没有真正虚拟方法(但具有正确定义的重载)的类。【参考方案3】:

C++ 和 C# 泛型不同(http://msdn.microsoft.com/en-us/library/c6cyy67b(v=VS.80).aspx,在您喜欢的搜索网站上搜索“c# c++ 泛型差异”)

简短:C# 编译器必须通过查看类本身来创建具有所有类型匹配的完整 GenWriter&lt;T&gt; 类。所以它不知道 T 是否只会是 int/string 或任何其他类型。

C++ 编译器通过查看泛型 GenWriter&lt;int&gt; 的实例化和声明 GenWriter&lt;T&gt; 创建实际类,然后为该特定实例创建类。

【讨论】:

【参考方案4】:

如果有人要调用GenWriter(5.0),这将被推断为GenWriter&lt;double&gt;(5.0),而Write(T x)内部的方法调用将变为:

public static void Write(double x)  Poly.WriteVal(x); 

WriteVal 没有重载,它需要双倍。编译器通知您WriteVal 没有有效的重载。

C# 泛型和 C++ 模板并不完全等价。

【讨论】:

【参考方案5】:

您不能在 C# 中这样做,因为编译器在编译时不知道 x 的类型。

在不知道 T 的实际类型的情况下,编译器会担心您可能打算执行 自定义 转换。最简单的解决方案是使用 as 运算符,这是明确的,因为它无法执行自定义转换。

更一般的解决方案是先转换为对象。由于装箱拆箱问题,这很有帮助:

return (int)(object) x;

请记住,C# 泛型与 C++ 模板不同。 C++ 模板是为每种类型分别编译的代码片段。而 C# 泛型是在汇编中编译的。

【讨论】:

以上是关于C# 中的多态、重载和泛型的主要内容,如果未能解决你的问题,请参考以下文章

c#泛型方法重载

方法重载和泛型

Java、静态方法绑定和泛型都包含一些方法重载

Java协变式覆盖(Override)和泛型重载(Overload)

C++ Primer 5th笔记(chap 16 模板和泛型编程)重载与模板

C++ Primer 5th笔记(chap 16 模板和泛型编程)重载模板和类型转换