I know about EventInfo.AddEventHandler(...)
method which can be used to attach handler to an event. But what should be done if i can not even define proper signature of the event handler, as in, i don't even have reference to the event args expected by the handler?
I will explain the problem with the proper code.
// Scenario when I have everything available in my solution, Zero Reflection Scenario.
internal class SendCommentsManager
{
public void Customize(IRFQWindowManager rfqWindowManager)
{
rfqWindowManager.SendComment += HandleRfqSendComment;
}
private void HandleRfqSendComment(object sender, SendCommentEventArgs args)
{
args.Cancel = true;
}
}
Now, I want to achieve the same objective by using reflection. I have been able to figure out most of it but when i attach a delegate to the event (using AddEventHandler
) it throws "Error binding to target method."
exception.
I understand the reason behind this exception, attaching a wrong delegate to an event. But there must be some way to achieve this.
internal class SendCommentsManagerUsingReflection
{
public void Customize(IRFQWindowManager rfqWindowManager)
{
EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
eventInfo.AddEventHandler(rfqWindowManager,
Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "HandleRfqSendComment"));
//<<<<<<<<<<ABOVE LINE IS WHERE I AM GOING WRONG>>>>>>>>>>>>>>
}
private void HandleRfqSendComment(object sender, object args)
{
Type sendCommentArgsType = args.GetType();
PropertyInfo cancelProperty = sendCommentArgsType.GetProperty("Cancel");
cancelProperty.SetValue(args, true, null);
}
}
I think your code is failing because the HandleRfqSendComment
is private. Instead you could directly create a delegate to that method, without passing its name to CreateDelegate
. You would then need to convert the delegate to the required type, using the following method :
public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(
targetDelegateType,
originalDelegate.Target,
originalDelegate.Method);
}
In your code, you could use this method as follows :
EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
Action<object, object> handler = HandleRfqSendComment;
Delegate convertedHandler = ConvertDelegate(handler, eventInfo.EventHandlerType);
eventInfo.AddEventHandler(rfqWindowManager, convertedHandler);
A small addition to the already awesome answers here. Here's a helper class you could use to subscribe to events with actions.
public static partial class ReactiveExtensions
{
public static EventHandler<TEvent> CreateGenericHandler<TEvent>(object target, MethodInfo method)
{
return (EventHandler<TEvent>)Delegate
.CreateDelegate(typeof(EventHandler<TEvent>),
target, method);
}
public static EventHandler CreateHandler(object target, MethodInfo method)
{
return (EventHandler)Delegate
.CreateDelegate(typeof(EventHandler),
target, method);
}
static void BindEventToAction(object target, EventInfo eventInfo, Delegate action)
{
MethodInfo method;
if (eventInfo.EventHandlerType.IsGenericType)
{
method = typeof(ReactiveExtensions)
.GetMethod(nameof(CreateGenericHandler))
.MakeGenericMethod(
eventInfo.EventHandlerType.GetGenericArguments());
}
else
{
method = typeof(ReactiveExtensions)
.GetMethod(nameof(CreateHandler));
}
Delegate @delegate = (Delegate)method.Invoke(null,
new object[] { action.Target, action.Method });
eventInfo.AddEventHandler(target, @delegate);
}
}
Here's a sample on how to use this:
public static partial class ReactiveExtensions
{
public static void Subscribe<T>(T source, string eventName)
{
EventInfo eventInfo = typeof(T).GetEvent(eventName);
Action<object, object> action = (s, e) =>
{
Console.WriteLine("Event Called");
};
BindEventToAction(source, eventInfo, action);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With