2013-02-28 133 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