Create a delegate from Methodinfo with generic parameter - c#

Based on this old but very usefull article by the legendary Jon Skeet, I wrote a few methods to genereate Action Delegates from MethodInfos for faster invokations. But I ran into an issue, where I am unable to make any progress. See the example below:
public class DataContainer
{
public List<int> myList;
public DataContainer()
{
myList = new List<int>() { 1, 2, 3, 4 };
}
}
public static class ExtensionMethods
{
public static void DoStuff<T>(this IList<T> items)
{
}
}
public class Example
{
private Action<object> doStuffQuickly;
public void Test()
{
var fieldInfo = typeof(DataContainer).GetField("myList");
var doStuffMethod = typeof(ExtensionMethods).GetMethod("DoStuff", BindingFlags.Public | BindingFlags.Static);
var listType = typeof(IList<>).MakeGenericType(fieldInfo.FieldType.GenericTypeArguments);
doStuffQuickly = QuickAccess.CreateWeakActionWithOneParam(doStuffMethod, listType);
var data = new DataContainer();
var list = fieldInfo.GetValue(data);
doStuffQuickly(list);
}
}
public static class QuickAccess
{
private static MethodInfo _weak1ParamActionCreator;
private static MethodInfo Weak1ParamActionCreator => _weak1ParamActionCreator
??= typeof(QuickAccess).GetMethod(nameof(CreateWeakExplicit1ParamAction), BindingFlags.Static | BindingFlags.NonPublic);
public static Action<object> CreateWeakActionWithOneParam(MethodInfo methodInfo, Type paramType0 = null)
{
var parameters = methodInfo.GetParameters();
paramType0 ??= parameters[0].ParameterType;
var creationMethod = Weak1ParamActionCreator.MakeGenericMethod(paramType0);
return (Action<object>)creationMethod.Invoke(null, new object[] { methodInfo });
}
private static Action<object> CreateWeakExplicit1ParamAction<TValue>(MethodInfo methodInfo)
{
var action = CreateStrongExplicit1ParamAction<TValue>(methodInfo);
return (object value) => action((TValue)value);
}
private static Action<TValue> CreateStrongExplicit1ParamAction<TValue>(MethodInfo methodInfo)
{
//Debug.Log(methodInfo.GetParameters()[0].ParameterType.Name + " <-> " + typeof(TValue).Name);
//Debug.Log(PrintTypes(methodInfo.GetParameters()[0].ParameterType.GenericTypeArguments) + " <-> "
// + PrintTypes(typeof(TValue).GenericTypeArguments));
return (Action<TValue>)Delegate.CreateDelegate(typeof(Action<TValue>), methodInfo);
}
private static string PrintTypes(Type[] types)
{
return string.Join(", ", types.Select(t => t.Name));
}
}
This fails with ArgumentException: method arguments are incompatible when trying to create the delegate.
The commented logs print IList`1 <-> IList`1 and T <-> Int32, which made me think that maybe
var listType = typeof(IList<>).MakeGenericType(fieldInfo.FieldType.GenericTypeArguments);
should instead simply be
var listType = typeof(IList<>);
but that is causing an InvalidOperationException: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
I don't know how to proceed from here. Any help will be appreciated.

If I understand the code correctly, listType would be a IList<int>, so DoStuff would be DoStuff<IList<int>>. I would expect it to be DoStuff<int>
So the correct version should be
var listItemType = fieldInfo.FieldType.GenericTypeArguments[0];
doStuffMethod = doStuffMethod.MakeGenericMethod(listItemType);
doStuffQuickly = QuickAccess.CreateWeakActionWithOneParam(doStuffMethod);
I would highly recommend running your code in a debugger and inspect your values in every step to confirm all your reflection types look like you expect. Reflection is difficult, so it is really useful to be able to confirm any assumptions.

Related

Private method testing in NUnit

Issue is simple but quite hard to bypass - I need to test private method without simply changing code to make it public. Doing it wouldn't be the end of the world, but this class contains one public method and set of private methods dedicated to that one public method, and changing it would be bad practice.
I have managed to found "solution" online, but it does not seem to work. Test fails with exception System.Reflection.TargetException : Object does not match target type..
Here is simplified code:
private Class _class;
private List<Item> _list;
private List<Item> _resultList;
[SetUp]
public void SetUp()
{
_class = new Class();
_list = new List<Item>();
_resultList = new List<Item>();
//do stuff to prepare data
}
[Test]
public void TestMethod_Equal()
{
var method = GetMethod("PrivateMethodName");
var result = method.Invoke(this, new object[] { _list }); //this private method needs `List<item>`
Assert.That(_resultList, Is.EqualTo(result));
}
private MethodInfo GetMethod(string methodName) //the online solution
{
if (string.IsNullOrWhiteSpace(methodName))
Assert.Fail("methodName cannot be null or whitespace");
var method = this._class.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
if (method == null)
Assert.Fail(string.Format("{0} method not found", methodName));
return method;
}
You need an instance of the class to be able to invoke the method against that instance. You also need to use the same type of said instance when locating the MethodInfo for the class. I've adapted your code to an example below:
void Main()
{
var myInstanceUnderTest = new MyConcreteClass();
var method = myInstanceUnderTest.GetType().GetPrivateMethod("SomePrivateMethod");
method.Invoke(myInstanceUnderTest, null);
}
public class MyConcreteClass
{
private void SomePrivateMethod()
{
Console.WriteLine("I ran!");
}
}
public static class Helpers
{
public static MethodInfo GetPrivateMethod(this Type myType, string methodName) //the online solution
{
if (string.IsNullOrWhiteSpace(methodName))
throw new Exception("methodName cannot be null or whitespace");
var method = myType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
if (method == null)
throw new Exception(string.Format("{0} method not found", methodName));
return method;
}
}

How to Invoke Method<T>(Func<Action<T>> action) with private type

I hope the code snipped illustrates my issue.
I need to Invoke the CallEvent method like it is in the out-commented line.
I have no access to the ThirdParty or AnotherThirdParty class.
This is as far as I come:
public class ThirdParty
{
private struct MsgType
{ }
private static void AnotherFunc(MsgType msg)
{ }
}
public class AnotherThirdParty
{
public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
{ }
}
public class MyClass
{
public static void Main()
{
Type MsgType = typeof(ThirdParty).GetNestedType(
"MsgType", BindingFlags.Instance | BindingFlags.NonPublic);
object msg = Activator.CreateInstance(MsgType);
MethodInfo CallEvent = typeof(AnotherThirdParty).GetMethod("CallEvent");
CallEvent = CallEvent.MakeGenericMethod(MsgType);
MethodInfo AnotherFunc = typeof(ThirdParty).GetMethod(
"AnotherFunc", BindingFlags.Static | BindingFlags.NonPublic);
CallEvent.Invoke(null, new object[] {???, msg});
//CallEvent<MsgType>((int x) => new Action<MsgType>(AnotherFunc), msg);
// I can't get my head around how to solve this (Action<msgtype>)
}
}
I also tried:
CallEvent.Invoke(null, new object[]
{
new Func<int, Action<object>>((int x) =>
new Action<object>((object y) =>
AnotherFunc.Invoke(null, new object[] { y }))),
msg
});
I get the following Exception:
System.ArgumentException: Object of type
'System.Func2[System.Int32,System.Action1[System.Object]]' cannot be
converted to type
'System.Func2[System.Int32,System.Action1[ThirdParty+MsgType]].
How should I proceed?
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.
Use Delegate.CreateDelegate method to construct an Action<MsgType> object. Construct Func<int,Action<T>> using Expression.Lambda<>:
var actionType = typeof(Action<>).MakeGenericType(MsgType);
var funcType = typeof(Func<,>).MakeGenericType(typeof(int), actionType);
var p1 = Expression.Parameter(typeof(int));
var p2 = Expression.Parameter(actionType);
var delegate = Expression.Constant(Delegate.CreateDelegate(actionType, AnotherFunc), funcType);
var lambda = Expression.Lambda(delegate, p1, p2);
CallEvent.Invoke(null, new object[] {
lambda.Compile()
, msg
});
This will work and will print an A but the function factory is a mistery to me so I just returned the created delegate. And this is compatible with .net standard 1.1
static void Main(string[] args)
{
Type MsgType = typeof(ThirdParty).GetNestedType(
"MsgType", BindingFlags.Instance | BindingFlags.NonPublic);
object msg = Activator.CreateInstance(MsgType);
MethodInfo CallEvent = typeof(AnotherThirdParty).GetMethod("CallEvent");
CallEvent = CallEvent.MakeGenericMethod(MsgType);
MethodInfo AnotherFunc = typeof(ThirdParty).GetMethod(
"AnotherFunc", BindingFlags.Static | BindingFlags.NonPublic);
var actionType = typeof(Action<>).MakeGenericType(MsgType);
var actionDelegate = AnotherFunc.CreateDelegate(actionType);
var param = Expression.Parameter(typeof(int));
var funcDelegate = Expression.Lambda(Expression.Constant(actionDelegate),param).Compile();
CallEvent.Invoke(null, new []{ funcDelegate, msg });
Console.ReadLine();
}
public class ThirdParty
{
private struct MsgType
{ }
private static void AnotherFunc(MsgType msg)
{
Console.WriteLine("A");
}
}
public class AnotherThirdParty
{
public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
{
action(1)(arg);
}
}

C# speedup method call of a generic class using expressions

I need to call an instance method of a generic class. The signature looks like this:
public class HandlerFactory
{
public static IHandler<T> Create<T>();
}
public interface IHandler<T>
{
T Read(Stream s);
void Write(Stream s, T v);
}
I managed to get it working by using expressions and DynamicInvoke. Sadly the performance of DynamicInvoke isn't that great. I can't cast the delegate to an Action<MemoryStream, T> because I don't know the type at compile time.
public class Test
{
public static void Write(MemoryStream s, object value)
{
var del = GetWriteDelegateForType(value.GetType());
// TODO: How to make this faster?
del.DynamicInvoke(s, value);
}
private static object GetHandlerForType(Type type)
{
var expr = Expression.Call(typeof(HandlerFactory), "Create", new[] { type });
var createInstanceLambda = Expression.Lambda<Func<object>>(expr).Compile();
return createInstanceLambda();
}
private static Delegate GetWriteDelegateForType(Type type)
{
var handlerObj = GetHandlerForType(type);
var methodInfo = handlerObj.GetType().GetMethod("Write", new[] { typeof(MemoryStream), type });
var arg1 = Expression.Parameter(typeof(MemoryStream), "s");
var arg2 = Expression.Parameter(type, "v");
var handlerObjConstant = Expression.Constant(handlerObj);
var methodCall = Expression.Call(handlerObjConstant, methodInfo, arg1, arg2);
var lambda = Expression.Lambda(methodCall, arg1, arg2);
return lambda.Compile();
}
}
Please note, I didn't benchmark the lambda generation, just the call to DynamicInvoke.
Is there any way to replace DynamicInvoke with something faster?
Update: I evaluated the 3 answers which contained code samples and choose to go with Lasse V. Karlsen answer because of the simplicity.
(Note on Grax's code: despite caching the MakeGenericMethod call it seems to be way slower than wrapping Invoke in a delegate)
Method | Median | StdDev |
------------------- |-------------- |----------- |
MyLambda | 1,133.2459 ns | 25.1972 ns |
ExplicitCall | 0.6450 ns | 0.0256 ns |
Test2DelegateLasse | 10.6032 ns | 0.2141 ns |
LambdaGroo | 10.7274 ns | 0.1099 ns |
InvokeGrax | 349.9428 ns | 14.6841 ns |
The way to do this is to go through a proper generic method, wrapping up a cast from object to the T, and skipping the entire dynamic invoke.
From your code in the pastebin, here's a new version of your Test class:
public class Test2
{
private static readonly Action<MemoryStream, object> del;
static Test2()
{
var genericCreateMethod = typeof(Test2).GetMethod("CreateWriteDelegate", BindingFlags.Static | BindingFlags.NonPublic);
var specificCreateMethod = genericCreateMethod.MakeGenericMethod(typeof(Model));
del = (Action<MemoryStream, object>)specificCreateMethod.Invoke(null, null);
}
public static void Write(MemoryStream s, object value)
{
del(s, value);
}
private static Action<MemoryStream, object> CreateWriteDelegate<T>()
{
var handler = HandlerFactory.Create<T>();
return delegate (MemoryStream s, object value)
{
handler.Write(s, (T)value);
};
}
}
On my machine your code, with the above as well executes as:
Your test: 1285ms
My test: 20ms
Explicit: 4ms
Write a generic method and use MakeGenericMethod with Invoke to call it.
Store the method you want to call in a static variable so the GetMethod call only needs to happen once.
Then call MakeGenericMethod on that MethodInfo and Invoke on the result.
private static MethodInfo GenericWriteMethod =
typeof(Test).GetMethod("GenericWrite", BindingFlags.NonPublic | BindingFlags.Static);
public static void Write(MemoryStream s, object value)
{
GenericWriteMethod
.MakeGenericMethod(value.GetType())
.Invoke(null, new object[] { s, value });
}
private static void GenericWrite<T>(MemoryStream s, T value)
{
HandlerFactory.Create<T>().Write(s, value);
}
In my testing, this made it over 100 times faster.
You should simply create an Action<Stream, object> instead:
static Action<Stream, object> GetWriteDelegateForType(Type type)
{
// get the actual generic method
var handlerObj = GetHandlerForType(type);
var methodInfo = handlerObj
.GetType()
.GetMethod("Write", new[] { typeof(MemoryStream), type });
// but use (Stream, object) parameters instead
var streamArg = Expression.Parameter(typeof(Stream), "s");
var objectArg = Expression.Parameter(typeof(object), "v");
// this will cast object to T
var tCast = Expression.Convert(objectArg, type);
var handlerObjConstant = Expression.Constant(handlerObj);
var body = Expression.Call(handlerObjConstant, methodInfo, streamArg, tCast);
var lambda = Expression.Lambda<Action<Stream, object>>(body, streamArg, objectArg);
// and compile to an actual Action<Stream, object>
return lambda.Compile();
}
And then you just call it like a plain delegate:
static void Write(MemoryStream s, object value)
{
var action = GetWriteDelegateForType(value.GetType());
action(s, value);
}
It would be also good idea to cache the delegate:
static readonly ConcurrentDictionary<Type, Action<Stream, object>> _cache =
new ConcurrentDictionary<Type, Action<Stream, object>>();
static void Write(MemoryStream s, object value)
{
var type = value.GetType();
var action = _cache.GetOrAdd(type, GetWriteDelegateForType);
action(s, value);
}
You could just make it an Action<MemoryStream, object> and simply use Expression.Convert() to change type of v from object to type.
For performance gain, you could just store those Actions in some Type-keyed dictionary (concurrent?), but the expressions would require some changes to Create the the handlers inside of them.
Adding another answer because this one is significantly different than my first one.
TLDR: Add a non-generic interface, create a dictionary to cache the handler for a type, call the handler using a non-generic Write method.
Add a non-generic interface to ExplicitHandler to make it easier to interact with in non-generic code.
public interface IHandler
{
void Write(Stream s, object v);
}
Implement the non-generic Write method in ExplicitHandler to cast to T and call the generic Write
void IHandler.Write(Stream s, object v)
{
Write(s, (T)v);
}
Cache the handlers in a dictionary
public class Test
{
static Dictionary<Type, IHandler> delegates = new Dictionary<Type, IHandler>();
public static void Write(MemoryStream s, object value)
{
IHandler handler;
var type = value.GetType();
if (!delegates.TryGetValue(type, out handler))
{
handler = (IHandler)typeof(HandlerFactory).GetMethod(nameof(HandlerFactory.Create)).MakeGenericMethod(type).Invoke(null, null);
delegates[type] = handler;
}
handler.Write(s, value);
}
}
Rewrite of your source here: http://pastebin.com/hmfj2Gv2

How to create delegate of Func<a,b> when a's method has parameters

I am trying to call a method that takes in a function, but using relection. I've gotten it to work when the method is parameterless but I can't figure out how to invoke it when there are arguments. This is a contrived simplified example, but this boils down the problem. I will not know the arguments to Add until runtime.
Any pointers? Do I have to use expression trees? Is there an easier way to do this?
public void Main()
{
//works
CallFunction(typeof (Processor), "Count");
//I don't understand what I need to modify to make add work
CallFunction(typeof (Processor), "Add");
}
public void CallFunction(Type type, string methodToCall)
{
var targetMethod = type.GetMethod(methodToCall);
var constructedType = typeof (MethodCaller<>).MakeGenericType(type);
dynamic target = Activator.CreateInstance(constructedType);
var method = constructedType.GetMethod("Do").MakeGenericMethod(targetMethod.ReturnType);
var func = typeof (Func<,>).MakeGenericType(type, targetMethod.ReturnType);
var toCall = Delegate.CreateDelegate(func, targetMethod);
method.Invoke(target, new object[] { toCall });
}
public class Processor
{
public int Count()
{
return 1;
}
public int Add(int toAdd)
{
return 1 + toAdd;
}
}
public class MethodCaller<TParm> where TParm : new()
{
public TResult Do<TResult>(Func<TParm, TResult> func)
{
return func(new TParm());
}
}
Like type.InvokeMember(method_name, System.Reflection.BindingFlags.InvokeMethod, null, type_instance, new object[] { param1, param2, param3 }); ?
https://msdn.microsoft.com/en-us/library/66btctbe(v=vs.110).aspx
Actually whole CallFunction method can be simplified like this:
public void CallFunction(Type type, string methodToCall, params object[] args)
{
// Why this part is so complex? Do I miss something?
//var constructedType = typeof (MethodCaller<>).MakeGenericType(type);
//dynamic target = Activator.CreateInstance(constructedType);
var target = Activator.CreateInstance(type);
var result = type.InvokeMember(method_name, System.Reflection.BindingFlags.InvokeMethod, null, target, args);
// ... do something with result if you need ...
}
If you need MethodCaller.Do, but can sacrifice types on signature (or some performance on boxing/unboxing for DoTyped)
public void CallFunction(Type type, string methodToCall, params object[] args)
{
var delegate_wrapper = new Func<object, object>(
instance => type.InvokeMember(methodToCall, BindingFlags.InvokeMethod, null, instance, args)
);
var target_method = type.GetMethod(methodToCall);
var mc_custom_type = typeof (MethodCaller<>).MakeGenericType(type);
var mc_instance = Activator.CreateInstance(mc_custom_type);
var mc_custom_method = mc_custom_type.GetMethod("Do").MakeGenericMethod(target_method.ReturnType);
mc_custom_method.Invoke(mc_instance, new object[] { delegate_wrapper });
}
public class MethodCaller<TParm> where TParm : new()
{
public TResult DoTyped<TResult>(Func<TParm, TResult> func)
{
return Do<TResult>(oinstance=>func((TParm)oinstance));
}
public TResult Do<TResult>(Func<object, object> func)
{
Console.WriteLine("I AM DO");
return (TResult)func(new TParm());
}
}
Have you tried looking at Impromptu framework. It is primarily for duck typing but the library provide rather slick api to access methods and pass arguments.
https://github.com/ekonbenefits/impromptu-interface/wiki/UsageReallyLateBinding

I want to get a method by reflection

I want to get a method by reflection,but the method has multiple overloads..and the method's parameters is a generic type..How can I do?
class Program
{
static void Main(string[] args)
{
test obj = new test();
Type[] types = { typeof(string) };
var method = obj.GetType().GetMethod("Say", BindingFlags.Instance | BindingFlags.NonPublic, null, types, null);
method.Invoke(obj, new object[] { "hello world" });
Console.ReadKey();
}
}
public class test
{
private void Say(string value)
{
Console.WriteLine(value);
}
private void Say<T>(T t)
{
Console.WriteLine(t);
}
}
I can get private void Say(string value)..but I want to get private void Say<T>(T t)..
Type[] types = { typeof(string) }; Here how to set up here?
Based on the comments, I would rather go for a solution like:
public static MethodInfo GetGenericMethod(object obj, string methodName, Type genericParameter)
{
var method = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(m => m.Name.Equals(methodName) && m.IsGenericMethod);
return method.MakeGenericMethod(genericParameter);
}
And then call it with something like:
GetGenericMethod(obj, "Say", typeof(string));
You will then be able to invoke it like any other methods.
There are you ways.
Get all methods and find that one you're looking for
obj.GetType().GetMethods().Single(m=>m.GetParameters().Length = /* parameter count*/ && /* next condition */);
Use Expression tree
In my opinion is nicer and less error prone but can be used only when you known which method you want in compile time.
You need some helper method that extract Method for expression tree.
public static MethodInfo Method(Expression<Action> methodAccess)
{
return ((MethodCallExpression)methodAccess.Body).Method;
}
Then you can use it like this:
Get(()=>obj.Say<string>("My param")).GetGenericMethodDefinition();
Compiler will take care of selecting method and helper method will extract MethodInfo from expression tree. GetGenericMethodDefinition() call will give you open-generic version of selected method.

Categories

Resources