How to Invoke Method<T>(Func<Action<T>> action) with private type
public class ThirdParty
{
private struct MsgType { }
private static void AnotherFunc(MsgType msg)
{
// Inserted to demonstrate getting here
Console.WriteLine($"HEY: {msg}");
}
}
public class AnotherThirdParty
{
public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
{
// Inserted to demonstrate calling the func and then
// the action
action(12)(arg);
}
}
public static void Main()
{
var msgTypeType =
typeof(ThirdParty).GetNestedType("MsgType", BindingFlags.NonPublic);
// This is the message type we're passing (presumably you'll do more with it)
var ourMsgTypeArg = Activator.CreateInstance(msgTypeType);
// Get the reference to the CallEvent method
var callEventMethod =
typeof(AnotherThirdParty).GetMethod("CallEvent", BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(msgTypeType);
// Get the reference to the AnotherFunc method
var anotherFunc =
typeof(ThirdParty).GetMethod("AnotherFunc", BindingFlags.NonPublic | BindingFlags.Static);
// Build the func to pass along to CallEvent
var func = CreateFunc(msgTypeType, anotherFunc);
// Call the CallEvent<MsgType> method.
callEventMethod.Invoke(null, new object[] {
func,
ourMsgTypeArg
});
}
private static Delegate CreateFunc(Type msgType, MethodInfo anotherFunc)
{
// The func takes an int
var intArg = Expression.Parameter(typeof(int));
// The action takes a msgType
var msgTypeArg = Expression.Parameter(msgType);
// Represent the call out to "AnotherFunc"
var call = Expression.Call(null, anotherFunc, msgTypeArg);
// Build the action to just make the call to "AnotherFunc"
var action = Expression.Lambda(call, msgTypeArg);
// Build the func to just return the action
var func = Expression.Lambda(action, intArg);
// Compile the chain and send it out
return func.Compile();
}
This code functions as you've requested and prints the following:
HEY: UserQuery+ThirdParty+MsgType
This seems to run:
MethodInfo miCreateDelegate = typeof(MethodInfo).GetMethod("CreateDelegate", new[] { typeof(Type), typeof(Object) });
var ActionType = typeof(Action<>).MakeGenericType(MsgType);
var lambdabody = Expression.Convert(Expression.Call(Expression.Constant(AnotherFunc), miCreateDelegate, new[] { Expression.Constant(ActionType), Expression.Constant(null) }), ActionType);
var intparm = Expression.Parameter(typeof(int));
var lambda = Expression.Lambda(lambdabody, intparm);
CallEvent.Invoke(null, new object[] {
lambda.Compile(),
msg
});
A more complete answer is how did I generate this? I used LINQPad to compile a simpler, similar expression substituting string
for MsgType
into an Expression
:
public static void afunc(string x) { }
Expression<Func<int, Action<string>>> lambda = (int x) => new Action<string>(afunc);
Then I used the LINQPad Dump()
function to output the expression tree.
lambda.Dump();
Then some spelunking in MSDN Expression documentation gave me the right static methods to create the pieces. I already knew how to instantiate generic types from an extension method for LINQPad that creates anonymous types on the fly to extend Dump()
to exclude fields from anonymous objects, and I knew how to create lambdas from an extension method that extends LINQ with a proper SQL-translatable Left and Right Join operation.