2011-05-25 26 views
4

我有以下代碼。我期待它打印:爲什麼這個通用在編譯時沒有解決?

A 
B 
C 
DONE 

,而不是它打印

P 
P 
P 
DONE 

爲什麼呢?

UPDATE
我不是要求解決方案。我想知道爲什麼會發生這種情況。我認爲仿製藥在編譯時已經解決了。從我可以告訴它應該能夠在編譯時解決這些適當的方法,但顯然它不是,我不明白爲什麼。我正在尋找解釋原因的解釋,而不是解決方法。

這裏是代碼:

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<string> Values { get; set; } 
    } 
    class Program 
    { 
     void Validate<T>(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<Parent> { 
       new ChildA(), new ChildB(), new ChildC() }; 
      Validate<ChildA>(list[0]); 
      Validate<ChildB>(list[1]); 
      Validate<ChildC>(list[2]); 
     } 
     public static void Main() 
     { 
      new Program().Run(); 
      Console.WriteLine(); 
      Console.WriteLine("DONE"); 
      Console.ReadLine(); 
     } 
    } 
} 
+3

看看這篇博客文章由Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx – juharr 2011-05-25 16:50:32

回答

9

泛型是一個運行時的概念。這是他們與C++模板的主要區別,這是一個編譯時概念。

在這個方法中Validate<T>T總是在編譯時未知,即使調用者明確指定。 東西Validate<T>知道T是它從Parent下降。

更具體地說,泛型不能用於生成代碼。你想什麼在C將工作++,因爲當C++看到到Validate<ClassA>一個電話,它實際上重新編譯Validate<T>,所以模板成爲一種代碼生成的。在C#下,Validate<T>只編譯一次,所以泛型不能用作一種代碼生成。

在C++中,調用Validate<ClassA>將實例在編譯時的模板。

在C#中,調用Validate<ClassA>將instatiate在運行時的通用方法。

+2

這是值得指出的是,即使沒有思考,很可能在.NET中有,當在一個字符串傳遞由字母X和Y(如「XYXXYXY」),將調用類型'X <泛型方法的方法Y >>>>>>',有效地證明了單個.NET程序可能使用的不同類的數量基本上是無限的 - 這與C++的情況非常不同,在這種情況下,每種不同類型都需要與之相關的代碼。 – supercat 2013-07-18 22:08:06

5

重載決策以在編譯時進行,而不是在運行時。

通常的解決辦法是使用這裏簡單的虛擬調度:

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<Parent> { new ChildA(), new ChildB() }; 
    list[0].Validate(); // prints "A" 
    list[1].Validate(); // prints "B" 
} 
+1

這按預期工作,但它與訪客無關。這是簡單的虛擬調度。 – Jon 2011-05-25 16:50:53

2

該項目將始終驗證類型父。

泛型背後的想法是,你沒有類型特定的代碼..因此它的「通用」部分。

您正在限制類的一個分支,在這種情況下,Parent和它的所有下降。這意味着代碼應該像傳入的對象是Parent類型一樣執行。

正如dtb所說,訪客模式可以在這裏適用。

另一個想法是簡單地有一個接口,它定義了每個類必須支持的Validate()方法。我認爲這將是一條更好的路線。

2

假設您可以使用C#4.0,您可以通過使用「dynamic」關鍵字來擊敗C#編譯器將執行的靜態重載解析。簡單地改變你的驗證功能:

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

和輸出將是:

C:\tmp>temp.exe 
A 
B 
C 

DONE 

我剛剛得知這個自己從@ juharr的鏈接埃裏克利珀的博客。詳細瞭解dynamic type on msdn