如何在C#中使用DynamicObject实现事件访问器

我正在尝试使用C#的DynamicObject为Qt的类系统实现一个通用的Wrapper-Class。 但是,我想写下面的代码:

dynamic obj = new SomeWrapperClass(....); // This extends DynamicObject obj.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); 

以上是根据VS2010的有效代码(需要显式转换为Action),但是我如何使用DynamicObject的方法“捕获”该语句?

我尝试实现TryGetMember()并为语句调用它,但我不知道我必须返回什么才能使它工作。

任何提示?

Reflector是你的朋友。 为第二行生成的代码看起来像这样(大约):

 if(Binder.IsEvent("OnMyEvent", typeof(SomeWrapperClass))) { Binder.InvokeMember("add_OnMyEvent", obj, myAction); } else { var e = Binder.GetMember("OnMyEvent", obj); var ae = Binder.BinaryOperation(ExpressionType.AddAssign, e, myAction); Binder.SetMember("OnMyEvent", obj, ae); } 

如果你不能为OnMyEvent使用真实事件(在这种情况下你可以依靠默认的DynamicObject实现),那么你需要返回一些实现AddAssign东西,返回类似多播委托的东西。 如果可能的话,我会建议前者……

为了好玩,这是一个将OnMyEvent动态绑定到OnMyOtherEvent的hackish示例:

 public class SomeWrapperClass : DynamicObject { public event Action OnMyOtherEvent; public override bool TryGetMember(GetMemberBinder binder, out object result) { if (binder.Name == "OnMyEvent") { result = OnMyOtherEvent; return true; } return base.TryGetMember(binder, out result); } public override bool TrySetMember(SetMemberBinder binder, object value) { if (binder.Name == "OnMyEvent" && value is Action) { OnMyOtherEvent = (Action)value; return true; } return TrySetMember(binder, value); } public void Test() { if (OnMyOtherEvent != null) OnMyOtherEvent(); } private static void TestEventHandling() { dynamic obj = new SomeWrapperClass(); // This extends DynamicObject obj.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); obj.Test(); } } 

使用reflection调用您的Action

 dynamic o = new SomeWrapperClass(); o.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); var a = typeof(SomeWrapperClass).GetField("OnMyEvent", BindingFlags.Instance | BindingFlags.NonPublic); (a.GetValue(o) as Action).Invoke(); 

输出:做点什么!

我认为你与代表们混淆了一些事件。 事件是有效的委托,但你不能使用’add’和’remove’访问者与委托 – 但+ =和 – =对两者都一样。

 obj.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); 

这基本上是将一个目标添加到委托的调用列表中,因此委托必须具有类似的类型(在这种情况下是无参数的Action委托)。

建议的实施如下:

 private Action actiondelegate = (Action)(() => {}); public override bool TryGetMember(GetMemberBinder binder, out object result) { if (binder.Name == "OnMyEvent") { result = actiondelegate; return true; } } 

请注意,在Action委托中需要一个空的Action – 这是因为如果它为null,则TryGetMemberTrySetMember无法正常工作。