2010-04-15 70 views
2

我有一個容器類,它具有受限於某些基類的泛型參數。提供給泛型的類型是基類約束的子類。子類使用方法隱藏(新)來改變方法從基類的行爲(不,我不能讓它變成虛擬的,因爲它不是我的代碼)。我的問題是'new'方法沒有被調用,編譯器似乎認爲提供的類型是基類,而不是sub,就好像我已經把它轉換爲基類。如何在通用約束類中使用方法隱藏(新)

很明顯,我誤解了一些根本性的東西。我認爲通用where T: xxx是一個約束,而不是一個upcast類型。

此示例代碼基本上演示了我在說什麼。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace GenericPartialTest 
{ 
    class ContextBase 
    { 
     public string GetValue() 
     { 
      return "I am Context Base: " + this.GetType().Name; 
     } 

     public string GetOtherValue() 
     { 
      return "I am Context Base: " + this.GetType().Name; 
     } 

    } 

    partial class ContextSub : ContextBase 
    { 
     public new string GetValue() 
     { 
      return "I am Context Sub: " + this.GetType().Name; 
     } 
    } 

    partial class ContextSub 
    { 
     public new string GetOtherValue() 
     { 
      return "I am Context Sub: " + this.GetType().Name; 
     } 
    } 

    class Container<T> where T: ContextBase, new() 
    { 
     private T _context = new T(); 

     public string GetValue() 
     { 
      return this._context.GetValue(); 
     } 

     public string GetOtherValue() 
     { 
      return this._context.GetOtherValue(); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Simple"); 
      ContextBase myBase = new ContextBase(); 
      ContextSub mySub = new ContextSub(); 

      Console.WriteLine(myBase.GetValue()); 
      Console.WriteLine(myBase.GetOtherValue()); 
      Console.WriteLine(mySub.GetValue()); 
      Console.WriteLine(mySub.GetOtherValue()); 

      Console.WriteLine("Generic Container"); 
      Container<ContextBase> myContainerBase = new Container<ContextBase>(); 
      Container<ContextSub> myContainerSub = new Container<ContextSub>(); 

      Console.WriteLine(myContainerBase.GetValue()); 
      Console.WriteLine(myContainerBase.GetOtherValue()); 
      Console.WriteLine(myContainerSub.GetValue()); 
      Console.WriteLine(myContainerSub.GetOtherValue()); 


      Console.ReadKey(); 
     } 
    } 
} 

編輯:

我想我的困惑來自於一個能做到這一點

class SomeClass<T> where T: AnotherType, new() 
{ 
    T foo = new T();  
} 

而且我預計TT即使我明白,編譯器會認爲T爲有AnotherType的界面。我認爲即使T的界面是在編譯時設置的,運行時也會發生T的打字。該T foo聲明似乎誤導這裏,因爲它是真正做

AnotherType foo = new T(); 

一旦我明白,這是不是真的宣佈fooT型,這是可以理解爲什麼new方法隱藏是行不通的。我不得不說這些。

+0

可能的重複http://stackoverflow.com/questions/774726/generics-accessing-new-members-not-hidden-members – 2010-04-15 22:59:38

回答

2

聲明爲new的方法與基類中具有相同名稱/簽名的方法沒有關係(從編譯器的角度來看)。這只是編譯器的方法,它允許您在派生類中定義不同的方法,這些方法與基類級聯中的方法共享一個簽名。

現在,就您的具體情況而言,請注意泛型必須編譯爲一組字節碼,而不考慮通用參數提供的類型。因此,編譯器只知道在泛型類型T上定義的方法和屬性 - 這是您在泛型約束中指定的基類型。編譯器對衍生類型中的new方法一無所知,即使您創建了派生類型作爲參數的泛型類型的實例。因此,泛型類中的調用將始終轉到基類型的方法。

關於新/虛擬/覆蓋有很多混淆;look at this SO question - 賈森和埃裏克的回答非常好。 Jon Skeet's answer也可能幫助你理解爲什麼你的實現行爲如此。

有你來解決此問題的兩種可能的方式:

  1. 在泛型類執行條件鑄(根據運行時類型信息),以派生類型(或接口)。這破壞了封裝並增加了不希望的耦合。如果執行得不好,它也很脆弱。
  2. 定義您在通用約束中使用的接口,該接口公開您所關心的方法。如果你所得到的代碼不是你可以改變的,這可能是不可能的。
+0

謝謝,我想我現在明白了。 – ongle 2010-04-16 00:03:28

1

我認爲this SO question我問了可能會幫助你。請看Jon Skeet在那裏的答案,爲什麼這是不可能的。

+0

是的,謝謝。我認爲這是更多的運行時間,但我想我錯了。 – ongle 2010-04-15 23:57:40

1

添加另一個圖層 - 從您的第三方類繼承您的泛型,而不是繼承自第三方的新類。在這個新班級中,您可以將所討論的方法定義爲新的虛擬班級。如果所有的代碼都不會直接引用第三方的類,它應該可以工作

+0

謝謝,是的,我知道一些解決問題的方法。我只是想了解核心問題,所以我不會再犯這個錯誤。 – ongle 2010-04-16 00:01:32