2012-07-04 264 views
0

我有以下設計問題編程應用程序在C#中。類設計的優雅解決方案

我有類兩個選自C派生。我無法更改他們的定義,因爲它們在外部程序集中定義,並且是而不是,定義爲部分

我試圖實現是不同天氣提供C對象是類型A或B的功能築底。當然,我不想使用if語句比較提供的對象的運行時類型。擴展方法可以使用嗎?我不這麼認爲。 任何解決方案? :)

+0

請顯示您目前爲止的內容以及爲什麼不符合您的要求。 – CodeCaster

+0

爲什麼你需要知道C是A還是B? –

+0

你如何獲得/創建A和B的實例?你是「新」還是通過某種工廠方法創建的? – tcarvin

回答

0

好的,我找到了答案。 動態關鍵字是這裏的線索。 我們可以這樣寫:

void Handle(C c) 
{ 
    dynamic cc = c; 
    HandleSpecific(cc); 
} 
void HandleSpecific(A a) 
{ 
//Specific behavior A 
} 
void HandleSpecific(B b) 
{ 
//Specific behavior B 
} 

缺點是當然的 - 異常的風險東陽運行時綁定在這裏引入輕微的性能損失。

0

你可以試試這個:

public static class CExtensions { 
    public static void DoIt(this B foo) {} 

    public static void DoIt(this A foo) {} 
} 

但是,我不認爲這會工作:

C x = new A(); 
x.DoIt(); 

我不認爲它會編譯,但是,我可以現在不測試它。

+0

你說得對,它不會編譯 –

2

應該可以通過使用泛型的擴展方法。 多種方法是可能的,但這是最簡單的。雖然你得到的是如果

public static void Foo<T>(this T objectC) 
    where T: C 
{ 
    if(typeof(T)==typeof(B){ //or for runtime check:  if(objectC is B) 
      //specific 
    } 
} 

你可以再調用富對A的任何實例或B.

你提到你鴕鳥政策想如果語句,但我不知道哪個擴大你試圖避免這一點?完全避免它的唯一方法是有2個擴展方法,一個用於A,另一個用於B(這又可以調用C的通用方法),但我認爲你試圖避免多個擴展方法?

編輯如果您絕對想要阻止if,那麼您必須使用Frederik帖子中顯示的多個擴展方法。您也可以爲基類添加擴展,只有在編譯期間未知類型時纔會調用該擴展。但是,這仍然需要一個如;)

public static void Foo(this A a) 
{ 
} 

public static void Foo(this B b) 
{ 
} 

public static void Foo(this C c) 
{ 
    if(c is A) 
     Foo((A)c); 
    else if(c is B) 
     Foo((B)c); 
    else 
     throw new NotSupportedException(c.GetType().FullName); 
} 

如果該類型在編譯時都知道,你可以簡單地使用一個連接B.

+0

我認爲這是最實用的解決方案。我同意* if *有點難看,但它不是那種,就是使用多種擴展方法。純粹主義者可能會選擇使用工廠來創建裝飾器類型的裝飾器模式實現,如果您構建的框架需要以開放 - 封閉原則的方式進行擴展(擴展方法違反OCP),則可能是合適的。 –

+0

我不想使用if語句,因爲我認爲比較運行時類型不是一種好的做法:) – Mic

+0

擔心良好實踐有點遲了。你怎麼能不告訴A從B看到他們的類型? –

0

2種擴展方法單純從可行性角度,可以使用反射和擴展方法。無論您的應用程序是否有意義,都應該評判。這裏解決方案....

class C 
{ 
} 

class A : C 
{ 
} 

class B : C 
{ 
} 

static class Extender 
{ 
    public static void M(this B b) 
    { 
     Console.WriteLine(" Extension method on B"); 
    } 

    public static void M(this A a) 
    { 
     Console.WriteLine(" Extension method on A"); 
    } 
} 

static void Main(string[] args) 
    { 
     C c = new A();// The actual instance here will be created using some factory. 
     object instance = Activator.CreateInstance(c.GetType()); 
     Type typeToFind = c.GetType(); 

     Type typeToQuery = typeof(Extender); 

     var query = from method in typeToQuery.GetMethods(BindingFlags.Static 
         | BindingFlags.Public | BindingFlags.NonPublic) 
        where method.IsDefined(typeof(ExtensionAttribute), false) 
        where method.GetParameters()[0].ParameterType == typeToFind 
        select method; 
     // You would be invoking the method based on its name. This is just a quick demo. 
     foreach (MethodInfo m in query) 
     { 
      m.Invoke(instance, new object[] { instance }); 
     } 

    } 
0

對我來說,這感覺就像一個設計缺陷。 如果你不能在兩個子類中有一個公共類,它們在不知道運行時類型的情況下重寫和更改功能,那麼你顯然在整體設計中會遇到一些問題。

根據運行時類型的不同,這些功能是否應該完全存在於這些類中?

0

你可以這樣做。用你需要的不同方法創建一個類。在同一個類中定義一個具有此方法簽名的委託,並保存一個Dictionary,其中的鍵是類型,以及值代表。最後,您可以爲您的類C創建一個擴展方法,它使用執行方法本身的對象的類型來查看字典,並執行正確的方法。事情是這樣的:

public static class CExtender 
{ 
    private static void DoItA(C anA) 
    { 
     MessageBox.Show("A"); 
    } 

    private static void DoItB(C aB) 
    { 
     MessageBox.Show("B"); 
    } 

    private static void DoItC(C aC) 
    { 
     MessageBox.Show("C"); 
    } 

    delegate void DoItDel(C aC); 

    private static Dictionary<Type, DoItDel> _doItDels; 

    private static Dictionary<Type, DoItDel> DoItDels 
    { 
     get 
     { 
      if (_doItDels == null) 
      { 
       _doItDels = new Dictionary<Type, DoItDel>(); 
       _doItDels[typeof(A)] = new DoItDel(DoItA); 
       _doItDels[typeof(B)] = new DoItDel(DoItB); 
      } 
      return _doItDels; 
     } 
    } 

    // the only public part is the extension method 
    public static void DoIt(this C aC) 
    { 
     DoItDel aDel; 
     if (DoItDels.TryGetValue(aC.GetType(), out aDel)) 
      aDel(aC); 
     else 
      DoItC(aC); 
    } 
} 
0

您可以在此情況下,使用Decorater模式

即你可以有2個你自己的新類,讓A1,B1都來自C(提供C不是密封類,如果這樣的話,解決方案會有點不同)。 A1和B1也需要相應地包裝A和B.您可以通過A1和B1的構造函數注入A和B的實例。

你也可以有一個共同的界面,A1和B1都有所啓示。接口需要有你需要的方法A1和B1以自己的方式實現。 (A1和B1可以根據需要將呼叫委派給A或B)

這樣,在您的客戶端代碼中,您可以通過其接口類型引用A1和B1實例,並執行接口中定義的常見操作,而無需知道他們實際的具體實現是什麼。

如果我不清楚是否需要代碼示例,請告知我。

+0

但我怎麼知道需要創建哪些類型(A1或A2)?它只在運行時才知道。 – Mic