2013-02-28 132 views
0

我剛剛熟悉了一下C#代表。可以通過「+ =」操作符將多個委託實例訂閱到委託。但是,是否也有可能擁有一個控制器類,它擁有第二個類中所有方法的委託,並自動添加方法,即不必爲每個方法單獨添加(甚至不知道)每個方法給相應的委託?訂閱代理類的所有方法

在簡化代碼(省略訪問修飾符等):

class Car 
{ 
    void Start(); 
    void Drive(); 
} 

// I would like to have the following class generated automatically 
// without needing to repeat all the methods of Car, i.e. 
// without declaring a delegate instance for each of them 
class CarController 
{ 
    delegate void DoSomething(); 

    DoSomething StartAll; 
    DoSomething DriveAll; 

    void Subscribe(Car anotherCar) 
    { 
     StartAll += anotherCar.Start; 
     DriveAll += anotherCar.Drive; 
    } 
} 

編輯: Rawling的解決方案是,我最喜歡的一個。它很簡單明瞭。作爲一個小小的調整,我已經嘗試過如何使用動態類型的對象,它確實有效:完成Controller和受控對象之間的解耦。的「動態」當然,這樣的用法是不是每個人的口味......

public class CallAller2 : HashSet<dynamic> 
{ 
    public void CallAll(Action<dynamic> action) 
    { 
     foreach (dynamic t in this) 
     { 
      try {action(t);} catch (RuntimeBinderException) {}; 
     } 
    } 
} 

class Bike 
{ 
    void Drive(); 
} 

CallAller2 ca = new CallAller2(); 
ca.Add(new Car()); 
ca.Add(new Bike()); 
ca.CallAll(c => c.Start()); // is ignored by Bike which does not implement it 
ca.CallAll(c => c.Drive()); 
+2

這聽起來像你正在描述你的想法*解決方案*的一個問題 - 你* *沒有告訴我們的問題。如果你能清楚地說明*問題而不是「解決方案」,那麼我們更有可能產生好的答案。 – 2013-02-28 07:50:22

+0

我想是的。只要簽名與委託相匹配,一切都應該起作用。你試過了嗎? – 2013-02-28 07:51:47

+0

@Damien_The_Unbeliever:我遇到的問題是我有一個數據模型(樹)必須與兩個不同的視圖(一個標準的TreeView和我的自定義SceneGraph)同步。當其中一個視圖發生變化時,數據必須更新,並且直接在代碼中操作數據時,視圖應該被更新。甚至在未來,我可能想要一個依賴於相同接口的撤銷管理器或腳本引擎。當我想像上面顯示的那樣做時,可能我想的太複雜了,但我想盡可能地分離所有不同的數據結構。 – oliver 2013-02-28 07:58:07

回答

0

一個問題叫做訪問現在我明白,這只是基本重建飽受詬病的。爲什麼不使用它,因爲它在那裏?


雖然這不會給你只是調用.StartAll.DriveAll的能力,你可以做一些簡單的

class CallAller<T> : HashSet<T> 
{ 
    public void CallAll(Action<T> action) 
    { 
     foreach (T t in this) 
     { 
      action(t); 
     } 
    } 
} 

var ca = new CallAller<Car>(); 
ca.Add(myFirstCar); 
ca.Add(mySecondCar); 

// Call a simple function 
ca.CallAll(c => c.Start()); 

// Call a function taking parameters 
ca.CallAll(c => c.SetRadio(88.1, RadioType.FM)); 

// Get return values... if you really need to. 
Dictionary<Car, int> returnValues = new Dictionary<Car, int>(); 
ca.CallAll(c => returnValues.Add(c, c.GetNumberOfTyres())); 

如果你想要的東西與實際的方法調用和智能感知,你我需要研究代碼生成 - 這是可能的,但我懷疑這是值得的麻煩。

+0

列表 .ForEach看起來非常好,但它可以嗎只接受一個參數的動作? (與我的OP中的簡單例子相反,我希望有幾個參數的方法)。此外,您只能將靜態方法傳遞給ForEach,不是嗎? – oliver 2013-02-28 09:10:31

+0

至於你的CallAller例子 - 在CallAll(Expression ...)之後似乎缺少一些東西......或者我們是否需要表達式重載(我不是lambda專家)? – oliver 2013-02-28 09:14:09

+0

@ user2118609好點,我改變了方法簽名,當我剪切/粘貼時錯過了原來的!我會解決這個問題,並在一個更多參數的例子... – Rawling 2013-02-28 09:20:16

0

我認爲這應該工作:

//編輯:不要簡化MethodInfo的MI1 = MI,否則你會得到改性封閉

static IList<Action> getDelegatesFromObject(Object obj) 
    { 
     Type type = obj.GetType(); 

     List<Action> Actions = new List<Action>(); 
     foreach (MethodInfo mi in type.GetMethods()) 
     { 
      MethodInfo mi1 = mi; 

      Actions.Add(
       () => mi1.Invoke(obj, new object[] {}) 
      ); 
     } 

     return Actions; 
    } 
+0

謝謝。這似乎進入了我想要的方向。但是,這也給我從Object(ToString,Equals)繼承的方法。你知道我可以如何限制它到我的自定義方法嗎? – oliver 2013-02-28 08:41:54

+0

我認爲你必須按methodname進行過濾。您可以通過mi1.IsVirtual過濾最接近的東西,只有GetType不是虛擬的。 – hcb 2013-02-28 08:47:51

+0

不幸的是,如果我通過方法名稱進行過濾,我必須在控制器中瞭解它們,這是我首先要避免的那種耦合...... :-) – oliver 2013-02-28 09:16:58