Add handler to any event with reflection, handle dynamically - c#

I'm writing a framework, one function of which involves remotely handling events (details of which I believe aren't relevant right now). What is needed though is the ability to hook an event handler to any event, given the target object reference and event name.
The method that handles the event will marshal/otherwise proxy the parameters to some handler code, then cast the return type to the event handler's return type. Effectively, it wraps the handler.
I believe that due to the fact that the event handler may be required to have any number of parameters of any type, the only way to handle a request to hook an event on-demand is to use reflection/emit (code generation) to build a custom proxy method. I'm posting this question to check that assumption and see if anyone has any other/neater ideas.
Currently, I create a DynamicMethod, then do this kind of thing. It works - almost - but the wrapper method that this dynamic proxy method calls must be static (the declaration is pretty much public static object GenericHandler(object[] parameters)); I don't want it to be. So my current task is to dynamically create a whole type, with this method, but also a field to store the instance of the object to call the wrapper on. But, that's beside the point (I think) - I want to check that my assumption about having to create dynamic code at all is correct or not. But, for ref:
// obj = target object, eventName = event name
var evtInfo = obj.GetType().GetEvent(eventName);
var handlerType = evtInfo.EventHandlerType;
var eventInvokeMethod = handlerType.GetMethod("Invoke");
var paramTypes = eventInvokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
var returnType = eventInvokeMethod.ReturnType;
var handlerMethod = new DynamicMethod(
"evtHandler_" + eventName,
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
returnType,
paramTypes,
typeof(my container class).Module,
false);
var il = handlerMethod.GetILGenerator();
// locals: [0] = parameter array
il.DeclareLocal(typeof(object[]));
// create an appropriately-sized array objects, for the parameters
il.Emit(OpCodes.Ldc_I4, (int)paramTypes.Count());
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc_0);
for (int i = 0; i < paramTypes.Length; i++)
{
// for the array: load the parameter into the same index of the array
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg, i);
if (!paramTypes[i].IsClass)
{
// box the value first..
il.Emit(OpCodes.Box, paramTypes[i]);
}
il.Emit(OpCodes.Stelem_Ref);
}
// call our generic handler with the array of parameters, then convert the return
// value to the target type via unboxing or a cast
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, wrapper.GetMethodInfo());
if (!returnType.IsClass)
{
il.Emit(OpCodes.Unbox_Any, returnType);
}
else
{
il.Emit(OpCodes.Castclass, returnType);
}
il.Emit(OpCodes.Ret);
If there are no responses and I get the above theory with wrapper type working, I'll post updated code.. but would love to hear about alternatives to this method if they are out there.

It was necessary to implement (and improve upon) the above code. With a dynamically-generated assembly, type and method, I can proxy any events fired to a general object HandleEvent(object[] parameters) method.

Related

Effective way to invoke generic interface method in runtime

I'm working on the some kind of EventSourcing architecture and have 2 main concepts in my app - events and handlers.
Events example:
class NewRecordCreated: EventMessage {...}
And there some handlers looks like:
class WriteDBHandler: IEventHandler<NewRecordCreated>, IEventHandler<RecordUpdated> {
public void Handle(NewRecordCreated eventMessage) {...}
public void Handle(RecordUpdated eventMessage) {...}
}
And also I have custom implementation of queue protocol which dispatch events to proper handlers. So basically on app startup I parse assembly and create mapping between event and handlers based on types.
So when I actually dispatching events to handlers I based on event type getting chain of handler's types - something like var handlerChain = [typeof(WriteDbHandler), typeof(LogHandler), typeof(ReadModelUpdateHandler)] and for each of those handlers I need to invoke it's instance, then cast it to proper interface (IEventHandler<>) and than invoke Handle method.
But I can't cast to generic interface, since it's not possible. I think about options of implementing non generic version of interface, but it's seems quite unpleasant for me to add extra method implementation each time, especially if there no any real reasons for it.
I think about dynamic invocation or reflection, but both of this variants seems have performance issues. Maybe you could advice me some suitable alternatives?
Using reflection
Rather than trying to cast to IEventHandler<>, you can instead use reflection to get a reference to the method you need to invoke. The code below is a good example. It simplifies the "queue protocol" for sake of brevity, but it should sufficiently illustrate the reflection that you need to do.
class MainClass
{
public static void Main(string [] args)
{
var a = Assembly.GetExecutingAssembly();
Dictionary<Type, List<Type>> handlerTypesByMessageType = new Dictionary<Type, List<Type>>();
// find all types in the assembly that implement IEventHandler<T>
// for some value(s) of T
foreach (var t in a.GetTypes())
{
foreach (var iface in t.GetInterfaces())
{
if (iface.GetGenericTypeDefinition() == typeof(IEventHandler<>))
{
var messageType = iface.GetGenericArguments()[0];
if (!handlerTypesByMessageType.ContainsKey(messageType))
handlerTypesByMessageType[messageType] = new List<Type>();
handlerTypesByMessageType[messageType].Add(t);
}
}
}
// get list of events
var messages = new List<EventMessage> {
new NewRecordCreated("one"),
new RecordUpdated("two"),
new RecordUpdated("three"),
new NewRecordCreated("four"),
new RecordUpdated("five"),
};
// process all events
foreach (var msg in messages)
{
var messageType = msg.GetType();
if (!handlerTypesByMessageType.ContainsKey(messageType))
{
throw new NotImplementedException("No handlers for that type");
}
if (handlerTypesByMessageType[messageType].Count < 1)
{
throw new NotImplementedException("No handlers for that type");
}
// look up the handlers for the message type
foreach (var handlerType in handlerTypesByMessageType[messageType])
{
var handler = Activator.CreateInstance(handlerType);
// look up desired method by name and parameter type
var handlerMethod = handlerType.GetMethod("Handle", new Type[] { messageType });
handlerMethod.Invoke(handler, new object[]{msg});
}
}
}
}
I compiled this and ran it on my machine and got what I believe are the correct results.
Using run-time code generation
If reflection is not fast enough for your purposes, you can compile code on-the-fly for each input message type and execute that.
The System.Reflection.Emit namespace has facilities for doing just that.
You can define a dynamic method (not to be confused with the dynamic keyword, which is something else), and emit a sequence if IL opcodes that will run each handler in the list in sequence.
public static Dictionary<Type, Action<EventMessage>> GenerateHandlerDelegatesFromTypeLists(Dictionary<Type, List<Type>> handlerTypesByMessageType)
{
var handlersByMessageType = new Dictionary<Type, Action<EventMessage>>();
foreach (var messageType in handlerTypesByMessageType.Keys)
{
var handlerTypeList = handlerTypesByMessageType[messageType];
if (handlerTypeList.Count < 1)
throw new NotImplementedException("No handlers for that type");
var method =
new DynamicMethod(
"handler_" + messageType.Name,
null,
new [] { typeof(EventMessage) });
var gen = method.GetILGenerator();
foreach (var handlerType in handlerTypeList)
{
var handlerCtor = handlerType.GetConstructor(new Type[0]);
var handlerMethod =
handlerType.GetMethod("Handle", new Type[] { messageType });
// create an object of the handler type
gen.Emit(OpCodes.Newobj, handlerCtor);
// load the EventMessage passed as an argument
gen.Emit(OpCodes.Ldarg_0);
// call the handler object's Handle method
gen.Emit(OpCodes.Callvirt, handlerMethod);
}
gen.Emit(OpCodes.Ret);
var del = (Action<EventMessage>)method.CreateDelegate(
typeof(Action<EventMessage>));
handlersByMessageType[messageType] = del;
}
}
Then, instead of invoking the handlers with handlerMethod.Invoke(handler, new object[]{msg}), you just call the delegate like any other, with handlersByMessageType[messageType](msg).
Full code listing here.
The actual code generation is done in the GenerateHandlerDelegatesFromTypeLists method.
It instantiates a new DynamicMethod, gets its associated ILGenerator, and then emits opcodes for each handler in turn.
For each handler type, it will instantiate a new object of that handler type, load the event message onto the stack, and then execute the Handle method for that message type on the handler object.
This is of course assuming that the handler types all have zero-parameter constructors.
If you need to pass arguments to the constructors, though, you'll have to modify it considerably.
There are other ways to speed this up even more.
If you relax the requirement to create a new handler object with every message, then you could just create the objects while generating the code, and load them.
In that case, replace gen.Emit(OpCodes.Newobj, handlerCtor) with gen.Emit(OpCodes.Ldobj, handlerObjectsByType[handlerType]).
That gives you two benefits:
1. you're avoiding an allocation on every message
2. you can instantiate the objects any way you want when you populate the handlerObjectsByType dictionary. You can even use constructors with parameters or factory methods.

TargetException on Invoke

I have a problem with my Invoke() throwing a TargetException.
public Controller(SystemUI ui, System system)
{
UI = ui;
System = system;
UI.CommandEntered += ParseCommand;
Commands = new Dictionary<string, Delegate>();
Commands.Add(":q", new Action(UI.Close));
}
I then call Commands[input[0]].Method.Invoke(this, input.ToArray<object>());, but it throws a TargetException with the message
Object does not match target type.
Do I need a cast?
I'm quite lost, and I'd appreciate any help!
Based on comments above, you are trying to invoke an Action (UI.Close), but you are passing an array of objects as parameters to this action, which has no parameters therefore incurring this exception.
Change...
input.toArray<object>()
to...
new object[0], or new object[] {} // or perhaps even just null may do the trick.

Call a function & return output to an external DLL in c#

I have a project which reads an external DLL through reflection and
System.Reflection.Assembly BuildDll = System.Reflection.Assembly.LoadFrom(System.AppDomain.CurrentDomain.BaseDirectory + BuildDllName);
Type BuildWindow = BuildDll.GetType(BuildFormType);
System.Reflection.MethodInfo constructors = BuildWindow.GetMethod("Initialize");
lMethod = BuildWindow.GetMethod("Submit");
TypeUserControl = Activator.CreateInstance(BuildWindow);
In this code I load the DLL and read its two methods Submit(to call this method to perform some actions) & Initialize(to pass the data required by DLL)
Now I have to return a function's result from my code to the DLL on a button event of that external DLL, there is a method in my code which returns the desired integer value
int GetValue(string id, int key)
In the external DLL a delegate is been defined as
private System.Delegate _BaseFunction;
public Delegate BaseFunction
{
set { _BaseFunction= value; }
}
On button Click event of external DLL result needed to be displayed
private void btnBaseInvoke_Click(object sender, RoutedEventArgs e)
{
object[] parameters = new object[2];
parameters[0]= Convert.ToString(txtParam30.Text.Trim());
parameters[1]= Convert.ToInt32(txtParam31.Text.Trim());
object obj = _BaseFunction.DynamicInvoke(parameters);
MessageBox.Show(Convert.ToString(obj));
}
What I am not getting is how will I initialize this delegate in my code & pass these parameters to my function?
The external DLL is a bit annoying in using a System.Delegate instead of a more specific delegate, but this is still very easy to solve.
To turn your GetValue method into a delegate, you can just do something like this:
var delegate = (Func<string, int, int>)GetValue;
That's it :)
Since you're using reflection (Do you have to? Isn't there an interface you could use instead or something? Do you have to load the assembly dynamically in the first place?), you may need to manually convert the resulting delegate to Delegate (which is just another cast), but I'm not sure if even that is necessary. Just call the setter on the property and pass it the delegate and you should be fine.
Did you try MethodBase.Invoke Method (Object, Object[])?
Maybe this will help you?

Creating a Delegate from methodInfo in Mono 2.8.2

Hi I am trying to create a messenger in Mono 2.8.2 - the subset used by Unity3d. I thought it would be nifty to create a helper to auto subscribe methods to the messenger when they are decorated with a "subscribe" attribute.
I've been scratching my head over this and have read many of the other related stack questions without a solution to my problem. Frankly, I don't know if I am doing something wrong or if this is a bug in Mono.
foreach (var methodInfo in methods)
{
var attr = methodInfo.GetAttribute<SubscribeAttribute>();
if (attr == null)
continue;
var parmas = methodInfo.GetParameters();
if (parmas.Length != 1)
{
Debug.LogError("Subscription aborted. Invalid paramters.");
continue;
}
var type = parmas[0].ParameterType;
// Crashes here
// ArgumentException: method argument length mismatch
// I have tried many combinations..
// Direct typing of the message type and dynamic typing
var action = (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), methodInfo);
// also does not work
// var dt = Expression.GetActionType(parmas.Select(o => o.ParameterType).ToArray());
// var action = Delegate.CreateDelegate(dt, methodInfo);
Subscribe(type, action, instance);
}
Any suggestions or work around would be appreciated.
Edit
The method signature looks like :
[Subscribe]
void OnMessage(object message){
// Hello World
}
Though, it was originally...
[Subscribe]
void OnTestMessage(TestMessage message){
// Hello World
}
It's a non-static method and you didn't provide a target object. Therefore Delegate.CreateDelegate will create an "open delegate" with an explicit this argument.
Because of the required this argument, it no longer matches the signature.

generic delegate creation using Expression in C#

Given below are two methods which create a delegate to set a field in a class. One method uses generics and the other does not.
Both the methods return a delegate and they work fine. But if I try to use the delegate that has been created inside the CreateDelegate method, then the non-generic delegate 'del' works fine. I can place a breakpoint on the return statement and invoke the delegate by writting del(222). But If I try to invoke the generic delegate 'genericDel' by writting genericDel(434), it throws an exception:
Delegate 'System.Action' has some invalid arguments
Can anyone explain this quirk.
class test
{
public double fld = 0;
}
public static void Main(string[] args)
{
test tst = new test() { fld = 11 };
Type myType = typeof(test);
// Get the type and fields of FieldInfoClass.
FieldInfo[] myFieldInfo = myType.GetFields(BindingFlags.Instance | BindingFlags.Public);
var a = CreateDelegate<double>(myFieldInfo[0], tst);
var b = CreateDelegate(myFieldInfo[0], tst);
Console.WriteLine(tst.fld);
b(5.0);
Console.WriteLine(tst.fld);
a(6.0);
Console.WriteLine(tst.fld);
}
public static Action<T> CreateDelegate<T>(FieldInfo fieldInfo, object instance)
{
ParameterExpression numParam = Expression.Parameter(typeof(T), "num");
Expression a = Expression.Field(Expression.Constant(instance), fieldInfo);
BinaryExpression assExp = Expression.Assign(a, numParam);
Expression<Action<T>> expTree =
Expression.Lambda<Action<T>>(assExp,
new ParameterExpression[] { numParam });
Action<T> genericDel = expTree.Compile();
//try to invoke the delegate from immediate window by placing a breakpoint on the return below: genericDel(323)
return genericDel;
}
public static Action<double> CreateDelegate(FieldInfo fieldInfo, object instance)
{
ParameterExpression numParam = Expression.Parameter(typeof(double), "num");
Expression a = Expression.Field(Expression.Constant(instance), fieldInfo);
BinaryExpression assExp = Expression.Assign(a, numParam);
Expression<Action<double>> expTree =
Expression.Lambda<Action<double>>(assExp,
new ParameterExpression[] { numParam });
Action<double> del = expTree.Compile();
//try to invoke the delegate from immediate window by placing a breakpoint on the return below: del(977)
return del;
}
I think I understood the issue; you are having problems invoking a generic delegate from the immediate window when the compile-time type of the delegate is an open generic type.
Here's a simpler repro:
static void Main() { Test<double>(); }
static void Test<T>()
{
Action<T> genericDel = delegate { };
// Place break-point here.
}
Now, if I try executing this delegate from within the Test method (by placing a break-point and using the immediate window) like this:
genericDel(42D);
I get the following error:
Delegate 'System.Action<T>' has some invalid arguments
Note that this not an exception like you have stated, but rather the 'immediate window version' of compile-time error CS1594.
Note that such a call would have failed equally at compile-time because there is no implicit or explicit conversion from double to T.
This is debatably a shortcoming of the immediate window (it doesn't appear to be willing to use additional 'run-time knowledge' to help you out in this case), but one could argue that it is reasonable behaviour since an equivalent call made at compile-time (in source code) would also have been illegal. This does appear to be a corner case though; the immediate window is perfectly capable of assigning generic variables and executing other code that would have been illegal at compile-time. Perhaps Roslyn will make things much more consistent.
If you wish, you can work around this like so:
genericDel.DynamicInvoke(42D);
(or)
((Action<double>)(object)genericDel)(42D);
The problem is that you are trying to invoke the delegate within the scope of the method that is creating it, before 'T' is known. It is trying to convert a value type (an integer) to the generic type 'T', which is not allowed by the compiler. If you think about it, it makes sense. You should only be able to pass in T as long as you are within the scope of the method that is creating the delegate, otherwise it wouldn't really be generic at all.
You need to wait for the method to return, then use the delegate. You should have no problem invoking the delegate after its completed:
var a = CreateDelegate<double>(myFieldInfo[0], tst);
var b = CreateDelegate(myFieldInfo[0], tst);
a(434);

Categories

Resources