2014-07-01 79 views
3

說我有下面的類結構:C#泛型:修改隱藏繼承成員在派生類中

public class A { 
    public string Name { get; set; } 
} 

public class B : A { 
    public new string Name { 
     get { return base.Name; } 
     set { base.Name = value + " set in B"; } 
    } 
} 

public static class Test { 
    public static void SetAndPrintName<T>(T value, string name) where T : A { 
     value.Name = name; 

     Console.WriteLine(value.Name); 
    } 
} 

這裏的結果,我期望從運行下面的代碼:

Test.SetAndPrintName(new A(), "a"); 
Test.SetAndPrintName(new B(), "b"); 
---- 
a 
b set in B 

相反,我得到:

a 
b 

由於沒有工作,我試着在鑄造,這是醜陋的,但和預期一樣:

public static void SetAndPrintName<T>(T value, string name) where T : A { 
    if (value is B) { 
     ((B)(object)value).Name = name; 
    } else { 
     value.Name = name; 
    } 

    Console.WriteLine(value.Name); 
} 
---- 
Test.SetAndPrintName(new A(), "a"); 
Test.SetAndPrintName(new B(), "b"); 
---- 
a 
b set in B 

我也試圖使Name財產虛擬/重寫和工作太:

public class A { 
    public virtual string Name { get; set; } 
} 

public class B : A { 
    public override string Name { 
     get { return base.Name; } 
     set { base.Name = value + " set in B"; } 
    } 
} 
---- 
Test.SetAndPrintName(new A(), "a"); 
Test.SetAndPrintName(new B(), "b"); 
---- 
a 
b set in B 

的問題是:爲什麼不new工作像其他?通用方法知道value的類型爲B,那麼爲什麼C#將它視爲一個A三分之一?

+0

'new'關鍵字本質上並不意味着'virtual'。通用實現不會改變這一點。 – haim770

回答

6

通用方法知道該值是類型B的

完全沒有它不編譯時。當編譯器看到value.Name時,它必須將其解析爲一個成員......並且唯一可用的成員是在A中聲明的成員。

想想你的兩個房產完全獨立 - 就好像它們有不同的名稱一樣。我們稱它們爲NameANameB

你的代碼是有效的:

public static void SetAndPrintName<T>(T value, string name) where T : A { 
    value.NameA = name; 
    Console.WriteLine(value.NameA); 
} 

這是有效的,因爲編譯器可以解析NameAA,並T被約束的A

如果試圖明確使用NameB,但是:

// Invalid 
public static void SetAndPrintName<T>(T value, string name) where T : A { 
    value.NameB = name; 
    Console.WriteLine(value.NameB); 
} 

...這是行不通的,因爲編譯器不能T解決NameB

當您使用虛擬財產,那麼你已經有了一個聲明的成員有其實施B覆蓋......而這恰恰是當你要修改的行爲你平常做的。

如果要使用不同的聲明成員基於執行時間型(不知道這一點,事前),你可以使用dynamic

public static void SetAndPrintName(dynamic value, string name) { 
    value.Name = name; 
    Console.WriteLine(value.Name); 
} 
+5

Aaaand我可以停止在我的答案上寫字:P – khellang

+0

Ahh好的,這是有道理的。我並沒有想到'new'會創建一個* cough *新屬性,而不是覆蓋現有的實現。感謝您的解釋! –

0

如果你不想使用動態關鍵字,你可能會引入IA接口,這兩種類型都繼承並使用IA作爲泛型方法的類型約束。這將產生你期望的輸出。

public interface IA 
{ 
    string Name { get; set; } 
} 

public class A : IA 
{ 
    public string Name { get; set; } 
} 

public class B : A, IA 
{ 
    public new string Name 
    { 
     get { return base.Name; } 
     set { base.Name = value + " set in B"; } 
    } 
} 

public static class Test 
{ 
    public static void SetAndPrintName<T>(T value, string name) where T : IA 
    { 
     value.Name = name; 

     Console.WriteLine(value.Name); 
    } 
}