为什么这个generics在编译时没有解决?

我有以下代码。 我希望它打印:

A B C DONE 

相反它打印

 P P P DONE 

为什么?

UPDATE
我不是要求解决方案。 我想知道为什么会这样。 我认为generics在编译时得到了解决。 从我可以告诉它应该能够在编译时解决这些正确的方法,但显然它不是,我不明白为什么。 我正在寻找解释原因,而不是解决方案。

这是代码:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication50 { class Parent { public string FieldName { get; set; } public string Id { get; set; } } class ChildA : Parent { public string FieldValue { get; set; } } class ChildB : Parent { public DateTime? Start { get; set; } public DateTime? End { get; set; } } class ChildC : Parent { public ICollection Values { get; set; } } class Program { void Validate(Parent item) where T : Parent { if (item is T) Validate(item as T); } void Validate(ChildA filter) { Console.WriteLine("A"); } void Validate(ChildB filter) { Console.WriteLine("B"); } void Validate(ChildC filter) { Console.WriteLine("C"); } void Validate(Parent filter) { Console.WriteLine("P"); // do nothing placeholder so the code will compile } ArgumentException Fail(Parent filter, string message) { return new ArgumentException(message, filter.FieldName); } void Run() { var list = new List { new ChildA(), new ChildB(), new ChildC() }; Validate(list[0]); Validate(list[1]); Validate(list[2]); } public static void Main() { new Program().Run(); Console.WriteLine(); Console.WriteLine("DONE"); Console.ReadLine(); } } } 

generics是一个运行时概念。 这是它们与C ++模板的主要区别,C ++模板是一个编译时的概念。

Validate方法中,即使调用者明确指定, T在编译时也始终未知的。 Validate 唯一知道T是它来自Parent

更具体地说,generics不能用于生成代码。 你正在尝试的是在C ++下工作,因为当C ++看到对Validate的调用时,它实际上重新编译了Validate ,因此模板成为一种代码生成。 在C#下, Validate只编译一次,因此generics不能用作一种代码生成。

在C ++下,对Validate的调用将在编译时实例化模板。

在C#下,对Validate的调用将在运行时实例化generics方法。

重载解析在编译时执行,而不是在运行时执行。

通常的解决方案是在这里使用简单的虚拟调度:

 class Parent { public virtual void Validate() { Console.WriteLine("P"); } } class ChildA : Parent { public override void Validate() { Console.WriteLine("A"); } } class ChildB : Parent { public override void Validate() { Console.WriteLine("B"); } } void Run() { var list = new List { new ChildA(), new ChildB() }; list[0].Validate(); // prints "A" list[1].Validate(); // prints "B" } 

该项目将始终被validation为父类型。

generics背后的想法是你没有类型特定的代码..因此它的“通用”部分。

您正在限制类的一个分支,在这种情况下,Parent和从它下降的所有内容。 这意味着代码应该执行,就像传入的对象是Parent类型一样。

正如dtb所说,访客模式可以在这里适用。

另一个想法是简单地有一个接口,它定义了每个类必须支持的Validate()方法。 我认为这是一条更好的选择。

假设您可以使用C#4.0,则可以通过使用“dynamic”关键字来破坏C#编译器将执行的静态重载决策。 只需将validationfunction更改为:

  void Validate(Parent item) where T : Parent { dynamic dyn = item; if (item is T) Validate(dyn); } 

输出将是:

 C:\tmp>temp.exe A B C DONE 

我刚刚从@ juharr链接到Eric Lippert的博客了解到这一点。 阅读有关msdn上动态类型的更多信息。