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

时间:2010-11-24 07:43:53

标签: c# .net visual-studio-2010 dynamic c#-4.0

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

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

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

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

任何提示?

3 个答案:

答案 0 :(得分:2)

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();
    }
}

答案 1 :(得分:0)

使用反射调用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();

输出: 做点什么!

答案 2 :(得分:0)

我认为你与代表们混淆了一些事件。事件是有效的委托,但你不能使用'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将无法正常工作。