Reputation: 756
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?
Upvotes: 3
Views: 1194
Reputation: 20451
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.
Upvotes: 0
Reputation: 43503
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!
Upvotes: 0
Reputation: 77500
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();
}
}
Upvotes: 2