C# speedup method call of a generic class using expressions - c#

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

Related

Create a delegate from Methodinfo with generic parameter

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.

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);
}
}

Creating a DynamicMethod to set a readonly field on any type

My aim is to create a delegate at runtime which can set any field (including readonly) in any reference type to a user-specified value. Unfortunately, my current implementation throws a VerificationException at runtime when the assemblies containing the types specify the [AllowPartiallyTrustedCallers] attribute.
AssemblyOne:
[assembly: AllowPartiallyTrustedCallers]
public class TypeOne
{
public TypeOne(TypeTwo typeTwoField)
{
this.TypeTwoField = typeTwoField;
}
public TypeTwo TypeTwoField { get; }
}
AssemblyTwo:
[assembly: AllowPartiallyTrustedCallers]
public class TypeTwo
{
public TypeTwo(int i)
{
this.Int = i;
}
public int Int { get; }
}
Main:
using System;
using System.Reflection;
using System.Reflection.Emit;
using AssemblyOne;
using AssemblyTwo;
namespace Main
{
class Program
{
public class MyType
{
public MyType(TypeOne typeOneField)
{
this.TypeOneField = typeOneField;
}
public TypeOne TypeOneField { get; }
}
static void Main(string[] args)
{
var fieldInfo = typeof(TypeOne)
.GetTypeInfo()
.GetField(
"<TypeTwoField>k__BackingField",
BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public);
var setter = (Action<TypeOne, TypeTwo>) GetReferenceSetter(fieldInfo);
var myType = new MyType(new TypeOne(new TypeTwo(1)));
// Throws VerificationException
setter(myType.TypeOneField, new TypeTwo(2));
}
public static Delegate GetReferenceSetter(FieldInfo field)
{
var delegateType = typeof(Action<,>)
.MakeGenericType(field.DeclaringType, field.FieldType);
var method = new DynamicMethod(
field.Name + "Set",
null,
new[] {field.DeclaringType, field.FieldType},
field.DeclaringType,
skipVisibility: true);
var emitter = method.GetILGenerator();
emitter.Emit(OpCodes.Ldarg_0);
emitter.Emit(OpCodes.Ldarg_1);
emitter.Emit(OpCodes.Stfld, field);
emitter.Emit(OpCodes.Ret);
return method.CreateDelegate(delegateType);
}
}
}
So MyType has a TypeOne which has a readonly TypeTwo. In this scenario, the DynamicMethod throws a VerificationException at runtime.
Is it possible to create such a delegate which works for any declaring type + field type which you throw at it? If so, how?
I realize that readonly fields ought never be set after construction, but the purpose of this is for deserialization & deep-copying.
Dynamic methods are quite limited in modifying their security. I suspect using an AssemblyBuilder instead may bypass the security checks, but I haven't tried that.
Instead, what may be able to achieve this is using other methods that access fields, in the TypedReference type.
public static unsafe void SetValue<T>(object inst, FieldInfo fi, T val)
{
var mi = typeof(TypedReference).GetMethod("InternalMakeTypedReference", BindingFlags.NonPublic | BindingFlags.Static);
var sig = MethodSignature.FromMethodInfo(mi);
var del = ReflectionTools.NewCustomDelegateType(sig.ReturnType, sig.ParameterTypes);
var inv = mi.CreateDelegate(del);
TypedReference tr;
var ptr = Pointer.Box(&tr, typeof(void*));
inv.DynamicInvoke(ptr, inst, new[]{fi.FieldHandle.Value}, fi.FieldType);
__refvalue(tr, T) = val;
}
FromMethodInfo and NewCustomDelegateType are from SharpUtils and are required to invoke InternalMakeTypedReference, which is able to obtain a reference to any field. Its signature looks like this:
private unsafe static extern void InternalMakeTypedReference(void* result, object target, IntPtr[] flds, RuntimeType lastFieldType);
You can replace the library methods with your own, they are needed just to build the appropriate delegate type (Action cannot be used due to the pointer). You cannot create the delegate type directly because of the internal RuntimeType. Surprisingly, DynamicInvoke works on the delegate, and the type reference is created.
However, to use this voodoo, I had to lower the security checks in the assembly a bit, so I am not sure if it also works in your assembly system:
[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1, SkipVerificationInFullTrust=true)]
Also please note that this code uses quite a number of undocumented features, and may stop working at any time. Use only at your own risk.
I have some exits code may be help you.
/// <summary>
/// create a getter delegate for a static field.
/// </summary>
/// <typeparam name="TField"></typeparam>
/// <param name="staticType">the type that contains the field. </param>
/// <param name="fieldName">the field that you want to get the value.</param>
/// <returns>the getter delegate.</returns>
public static Func<TField> CreateStaticFieldGetter<TField>(Type staticType, string fieldName)
{
var fieldInfo = staticType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
var body = Expression.Field(null, fieldInfo);
Expression<Func<TField>> lambda = Expression.Lambda<Func<TField>>(body);
return lambda.Compile();
}
public static Action<TField> CreateStaticFieldSetter<TField>(Type staticType, string fieldName)
{
ParameterExpression p1 = Expression.Parameter(typeof(TField), "p1");
var fieldInfo = staticType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
var leftExpr = Expression.Field(null, fieldInfo);
var body = Expression.Assign(leftExpr, p1);
Expression<Action<TField>> lambda = Expression.Lambda<Action<TField>>(body, p1);
return lambda.Compile();
}
public static Func<TTarget, TField> CreateInstanceFieldGetter<TTarget, TField>(string fieldName)
{
ParameterExpression p1 = Expression.Parameter(typeof(TTarget), "p1");
var fieldInfo = typeof(TTarget).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
var body = Expression.Field(Expression.Convert(p1, typeof(TTarget)), fieldInfo);
Expression<Func<TTarget, TField>> lambda = Expression.Lambda<Func<TTarget, TField>>(body, p1);
return lambda.Compile();
}
public static Action<TTarget, TProperty> CreateInstanceFieldSetter<TTarget, TProperty>(string filedName)
{
ParameterExpression p1 = Expression.Parameter(typeof(TTarget), "p1");
ParameterExpression p2 = Expression.Parameter(typeof(TProperty), "p2");
var member = typeof(TTarget).GetField(filedName, BindingFlags.NonPublic | BindingFlags.Instance);
var m1 = Expression.MakeMemberAccess(Expression.Convert(p1, typeof(TTarget)), member);
BinaryExpression body = Expression.Assign(m1, p2);
Expression<Action<TTarget, TProperty>> lambda = Expression.Lambda<Action<TTarget, TProperty>>(body, p1, p2);
return lambda.Compile();
}

C# FieldInfo reflection alternatives

I am currently using FieldInfo.GetValue and FieldInfo.SetValue quite a lot in my programm, which is significantly slowing up my programm.
For PropertyInfo I use the GetValueGetter and GetValueSetter methods so I only use reflection once for a given type. For the FieldInfo, the methods don't exist.
What is the suggested approach for FieldInfo?
EDIT:
I followed this useful link from CodeCaster's reply. This is a great search direction.
Now the "only" point that I don't get in this answer is how I can cache the getters / setters and re-use them in a generic way, using only the field's name - which is basically what the SetValue is doing
// generate the cache
Dictionary<string, object> setters = new Dictionary<string, object>();
Type t = this.GetType();
foreach (FieldInfo fld in t.GetFields()) {
MethodInfo method = t.GetMethod("CreateSetter");
MethodInfo genericMethod = method.MakeGenericMethod( new Type[] {this.GetType(), fld.FieldType});
setters.Add(fld.Name, genericMethod.Invoke(null, new[] {fld}));
}
// now how would I use these setters?
setters[fld.Name].Invoke(this, new object[] {newValue}); // => doesn't work without cast....
You could use Expressions to generate faster field accessors. If you want them to work on Object rather than the field's concrete type, you'd have to add casts (Convert) in the expression.
using System.Linq.Expressions;
class FieldAccessor
{
private static readonly ParameterExpression fieldParameter = Expression.Parameter(typeof(object));
private static readonly ParameterExpression ownerParameter = Expression.Parameter(typeof(object));
public FieldAccessor(Type type, string fieldName)
{
var field = type.GetField(fieldName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null) throw new ArgumentException();
var fieldExpression = Expression.Field(
Expression.Convert(ownerParameter, type), field);
Get = Expression.Lambda<Func<object, object>>(
Expression.Convert(fieldExpression, typeof(object)),
ownerParameter).Compile();
Set = Expression.Lambda<Action<object, object>>(
Expression.Assign(fieldExpression,
Expression.Convert(fieldParameter, field.FieldType)),
ownerParameter, fieldParameter).Compile();
}
public Func<object, object> Get { get; }
public Action<object, object> Set { get; }
}
Usage:
class M
{
private string s;
}
var accessors = new Dictionary<string, FieldAccessor>();
// expensive - you should do this only once
accessors.Add("s", new FieldAccessor(typeof(M), "s"));
var m = new M();
accessors["s"].Set(m, "Foo");
var value = accessors["s"].Get(m);

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