2016-12-25 143 views
4

有人可以向我解釋爲什麼下面的代碼輸出它的作用? 爲什麼T是第一個字符串,而不是Int32,爲什麼它在下一個輸出中是相反的情況?繼承和泛型類型設置

這讓人不解的是從interview with Eric Lippert

當我看到通過代碼,我真的不知道,如果它的將是一個Int32或String:

public class A<T> 
    { 
     public class B : A<int> 
     { 
      public void M() { System.Console.WriteLine(typeof(T)); } 
      public class C : B { } 
     } 
    } 

    public class P 
    { 
     public static void Main() 
     {    
      (new A<string>.B()).M(); //Outputs System.String 
      (new A<string>.B.C()).M(); //Outputs System.Int32 
      Console.Read(); 
     } 
    } 
+1

https://blogs.msdn.microsoft.com/ericlippert/2007/07/30/an-inheritance-puzzle-part-two/ –

回答

2

更改代碼略:

public class A<T> 
{ 
    public class B : A<int> 
    { 
     public void M() { System.Console.WriteLine(typeof(T)); } 
     public class C : A<T>.B { } 
    } 
} 

public class P 
{ 
    public static void Main() 
    {    
     (new A<string>.B.C()).M(); //Outputs System.String 
    } 
} 

注意我是如何改變C的基類BA<T>.B。這將輸出從System.Int32更改爲System.String

沒有這個,A<string>.B.C不是從A<string>.B得到的,而是從A<int>.B得出的,導致你看到的行爲。這是因爲一般來說,基類中定義的名稱可以通過非限定查找來獲得,名稱B在基類A<int>中定義。

+0

如果說一個班級如果距離它不超過一個「等級」就可以訪問輸入的T值,這是真的嗎?例如。 (new A .B())。M();輸出字符串,因爲B直接訪問A 這意味着提供的T覆蓋了B默認從其繼承的值? (new A .B.C())。M();輸出Int32,因爲一旦在C中,它離輸入的String的T值有2級距離,因此它使用默認的Int32繼承值。 –

+0

@Backwards_Dave不,事實並非如此,將'M'的定義放入'C'中會顯示它不是真的。 'A .B.M'打印'T',無論你打電話時多遠有多遠。棘手的部分只是由於誤導性的基類,'A .B.M'被調用而不是'A .B.M'。 – hvd

+0

讓我試着再解釋一遍。如果T向下流過繼承,它將覆蓋繼承中定義的任何默認T值。但是,只要有「向上」流,在這種情況下,當C調用C的父類中定義的M()時,指定的T值將丟失,並使用Int32的默認T值。而如果M()以C而不是B來聲明,那麼就不會有向上流動。不完全確定在這裏使用什麼詞,也許「流」不是很好,但我希望你知道我的意思。 –

4

方法MB打印typeof(T)A<T>AB的父級。

因此,不管B來自何處,M打印typeof(T)String

所以A<T>.B.M打印最接近AT

所以A<string>.B.M將打印string

現在,讓我們拓展表達A<string>.B.C,這相當於A<string>.B.A<int>.B(因爲CA<int>.B),所以方法A<string>.B.A<int>.B.M將打印最近T

A<string>.B.A<int>.B.M將打印int

+0

爲什麼C等於A .B?更確切地說,爲什麼String的輸入值T會丟失,並使用Int的默認值? –

+0

這不等於,它是如何解析類型的類型解析替代類型。 –

3

Introduction to Generics T也是在嵌套類可用。類別B的情況是嵌套在A中。另一方面,C嵌套爲B,並且B的T可用於C。如您所見,B的T爲int,並且調用C的方法將使用int作爲通用參數。

3

方法M()始終打印父類的類的泛型參數的類型:

所以(new A<string>.B.C()).M();應打印泛型類型的B這始終是int。 (你可以看到B總是A<int>

而且(new A<string>.B()).M();應打印string因爲BA<string>

+0

我不確定如果我沒有看到輸出,我可以乍一看正確的輸出,但看到結果後,檢測結果如何打印並不難。 Chu難題很容易解決巡邏;) –

3

有人可以向我解釋爲什麼下面的代碼輸出它做什麼?

問題的癥結在於確定Bclass C : B中的含義。考慮沒有泛型版本:(爲簡便起見,我會忽略了公衆。)

class D { class E {} } 
class J { 
    class E {} 
    class K : D { 
    E e; // Fully qualify this type 
    } 
} 

這可能是J.ED.E;這是什麼?解決名稱時,C#中的規則是查看基類層次結構,只有在失敗時才查看容器。 K通過繼承已經有了一個成員E,所以它不需要看它的容器,通過遏制來發現它的容器有一個成員E.

但我們看到拼圖有相同的結構;它只是泛型的混淆。我們可以把一般的像一個模板,只是寫出來的A-的字符串的結構和A-的-INT作爲類:

class A_of_int 
{ 
    class B : A_of_int 
    { 
    void M() { Write("int"); } 
    class C : B { } // A_of_int.B 
    } 
} 
class A_of_string 
{ 
    class B : A_of_int 
    { 
    void M() { Write("string"); } 
    class C : B {} // still A_of_int.B 
    } 
} 

現在應該很清楚爲什麼A_of_string.B.M()寫道:stringA_of_string.B.C.M()寫道:int