3

I am trying to implement a generic Wrapper-Class for Qt's class system using C#'s DynamicObject. However, I want to write the following code:

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

The above is valid code according to VS2010 (the explicit cast to Action is required), but how exactly do i "catch" that statement using DynamicObject's methods?

I tried implementing TryGetMember() and it gets called for the statement, but I have no idea what I have to return to make it work.

Any hints?

0

3 Answers 3

2

Reflector is your friend on this one. The code generated for your second line looks something like this (approximately):

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

If you can't use a real event for OnMyEvent (in which case you can lean on the default DynamicObject implementation), then you'll need to return something that implements AddAssign returning something like a multicast delegate. I'd suggest the former, if possible...

For fun, here's a hackish example that dynamically binds OnMyEvent to OnMyOtherEvent:

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();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

First of all, thanks a LOT for the hint about just looking at the IL it generates (well or a rather high-level representation). What I find quite interesting is how it handles the return value of GetMember. Couldn't I just use a static "mock" event to return from TryGetMember for all my events, then do the actual binding in TryInvokeMember? (for add_XYZEvent)?
That's what I would try, yes. I have no idea if it would work well, but if you get it figured out please share. :)
0

Invoke your Action with reflection:

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

Output: DO something!

Comments

0

I think you are confusing events with delegates. Events are effectively delegates, but you cannot use the 'add' and 'remove' accessors with delegates - however the += and -= works the same with both.

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

This is basically adding a target to the invocation list of a delegate, so that delegate must have a similar type (in this case a parameterless Action delegate).

The suggested implementation is below:

private Action actiondelegate = (Action)(() => {});

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

Note that you need an empty Action in your Action delegate - this is because if it is null the TryGetMember and TrySetMember wont work correctly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.