2013-05-08 78 views
10

考慮以下層次:通用包裝類

class A 
{ 
} 
class B : A 
{ 
    public void Foo() { } 
} 
class C : A 
{ 
    public void Foo() { } 
} 

這是一個第三方庫,我不能修改它。有沒有辦法我可以寫一些'通用模板包裝',將Foo()方法轉發給作爲構造函數參數傳遞的適當對象? 我最後寫以下,其中不使用仿製藥,似乎比較難看:

class Wrapper 
    { 
     A a; 
     public Wrapper(A a) 
     { 
      this.a = a; 
     } 

     public void Foo() 
     { 
      if (a is B) { (a as B).Foo(); } 
      if (a is C) { (a as C).Foo(); } 
     } 

    } 

我喜歡一些模板約束像Wrapper<T> where T : B or C

+1

我認爲主要的問題是,'富''不是'虛擬'在'A',對吧? – dasblinkenlight 2013-05-08 10:59:14

回答

15

如果A沒有Foo,你需要要麼使用dynamic(見Jon Skeet's answer),或使用一個小技巧與lambda表達式和重載:

class Wrapper { 
    private Action foo; 
    public Wrapper(B b) { 
     foo =() => b.Foo(); 
    } 
    public Wrapper(C c) { 
     foo =() => c.Foo(); 
    } 
    public void Foo() { 
     foo(); 
    } 
} 

現在你可以這樣做:

var wb = new Wrapper(new B()); 
wb.Foo(); // Call B's Foo() 
var wc = new Wrapper(new C()); 
wc.Foo(); // Call C's Foo() 

這對轉移什麼方法從目前的Foo打電話叫以創建Wrapper的那一刻,有可能挽救你一些CPU週期的決定。

+0

謝謝大家!我想我會爲你的解決方案尋求更多類似Foo的功能,爲簡潔起見,我省略了這些功能。我還必須在我的代碼中多次使用Wrapper,並且動態看起來過於內聯解決方案。 – amnezjak 2013-05-08 11:23:18

7

不,這兩個Foo方法就編譯器而言是完全不相關的。這樣做的不知道對各個類型先從最簡單的方法是使用動態類型:

public void Foo() 
{ 
    dynamic d = a; 
    // Let's hope there's a suitable method at execution time! 
    d.Foo(); 
} 

仿製藥不會幫助你在這裏,至於我可以告訴。這不像是有一些界面(至少沒有你顯示過),你可以限制T

你可以傳遞一個Action還有:

Wrapper wrapper = new Wrapper(b, b.Foo); 

這使得它稍微少主叫方便,但很一般......

+1

這是Skeet大肚子。你搖動Skeet。 – JSJ 2013-05-08 11:06:28

+0

合併喬恩的「行動傳遞」與dasblinkenlight的建議和T4模板,你應該得到非常合理和可維護的解決方案。 – quetzalcoatl 2013-05-08 11:11:53

0

我不願意表明它,但你不能修改庫。如果這不是性能關鍵,召回dynamic關鍵字:)

class Wrapper 
{ 
    public dynamic theAorBorC; 

    public Wrapper(A a){theAorBorC=a;} 
    public Wrapper(B b){theAorBorC=b;} 
    public Wrapper(C c){theAorBorC=c;} 

    // or even... 
    // public Wrapper(object anything){theAorBorC=anything;} 

    public void CallFoo() 
    { 
     theAorBorC.Foo(); 
    } 
} 

編輯:在其他情況下,我個人使用類似於dasblinkenlight所顯示的lambda - 來獲得編譯時檢查。它可以很容易地自動生成,即使用T4或任何其他文本生成器。

0

您可以創建一個在根級別包含Foo()方法的並行分層結構。
使用工廠方法可以爲任一類型創建包裝實例。對於這個工作,你需要知道你的A實例的確切類型,當你調用工廠方法

abstract class Wrapper { 
    public abstract void Foo(); 

    //factory methods 
    public Wrapper FromB(B instance) { 
     return new WrapperB(instance); 
    } 
    public Wrapper FromC(C instance) { 
     return new WrapperB(instance); 
    } 
} 

class WrapperB { 
    private B instance {get; set;} 
    public WrapperB(B instance) { 
     this.instance = instance; 
    } 

    public void Foo() { 
     instance.Foo(); 
    } 
} 
class WrapperC { 
    private C instance {get; set;} 
    public WrapperC(C instance) { 
     this.instance = instance; 
    } 

    public void Foo() { 
     instance.Foo(); 
    } 
} 

編輯:這是基本相同this answer