In C# I have following methods defined in given class (non static):
int MyMethod(ScriptEngine script, int a, int b) {
return a + b;
}
void MyMethod2(ScriptEngine script, string c) {
// do something with c
}
I want to create wrapping lambda / Action / Delegate / MethodInfo (all are accepted by script engine) that automatically passes ScriptEngine and this from given, predefined variables.
So far I've experimenting with:
// With overloads up to 16 template parameters
Action<T1> Wrap<T1>(Action<ScriptEngine, T1> func, ScriptEngine script) {
return (Action<T1>) ((t1) => func(script, t1));
}
But when called on MyMethod2 I've got The type arguments for method … cannot be inferred from the usage. Try specifying the type arguments explicitly. If I specify the template arguments explicity, it works, but I want to avoid such specification.
Is there any other way (not neccesery following my solution) I can create such wrapper automatically (or semi-automatically)?
It is worth mentioning that there is a dedicated abstract method void RegisterAll(ScriptEngine script) that can register required members of given subclass.
Here is an example of what I am trying to achieve:
class ScriptEngine { // Stub to have complete example, actual implementation is defined elsewhere
void RegisterApi(string name, MethodInfo methodInfo) { }
void RegisterApi(string name, Delegate delegateSelf) { }
}
class Api {
int MyMethod(ScriptEngine script, int a, int b) {
return a + b;
}
void MyMethod2(ScriptEngine script, string c) {
// do something with c
}
void RegisterAll(ScriptEngine script) {
// Is there any way to shorten this up (not providing MyMethod twice, not providing template arguments?)
script.RegisterApi(nameof(MyMethod), (Delegate)Wrap<string>(MyMethod, script));
}
}
The problem is how to improve this RegisterApi method so it:
Takes the method only once
Does not require specifying arguments via template method
There is actually another solution that does not involve emiting new Expressions (could fail on iOS!)
First, let us define following wrapper:
private class Wrapper
{
public readonly object container;
public readonly MethodInfo method;
public readonly ScriptEngine engine;
public Wrapper(object container, MethodInfo method, ScriptEngine engine)
{
this.container = container;
this.method = method;
this.engine = engine;
}
public Action CreateAction()
{
return () => method.Invoke(container, new object[] { engine });
}
public Action<T1> CreateAction<T1>()
{
return (arg1) => method.Invoke(container, new object[] { engine, arg1 });
}
// etc
}
Now you can register method like that:
var type = typeof(Wrapper);
var instance = Activator.CreateInstance(type, new object[] { container, methodInfo, engine });
MethodInfo methodActual = null;
if (methodInfo.ReturnType == typeof(void))
{
var methods = type.GetMethods().Where(x => x.Name == "CreateAction");
foreach (var method in methods)
{
if (method.GetGenericArguments().Length == methodInfo.GetParameters().Length - 1)
{
methodActual = method.MakeGenericMethod(methodInfo.GetParameters().Skip(1).Select(x => x.ParameterType).ToArray());
}
}
}
var actionToRegister = methodActual.Invoke(instance, new object[0]);
The solution is to use Expression Tree to create a simple delegate wrapper.
var parameterEngine = Expression.Constant(script, typeof(ScriptEngine));
var parameterCaller = Expression.Constant(this, this.GetType());
// Build parameters, skipping the first. Note that trailing "ToList" is important!
var parameters = methodInfo.GetParameters().Skip(1).Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList();
// Inject parameter
var mergedParameters = new List<Expression>();
mergedParameters.Add(parameterEngine);
mergedParameters.AddRange(parameters);
// Bind together
var call = Expression.Call(parameterCaller, methodInfo, mergedParameters);
var expression = Expression.Lambda(call, parameters);
var method = expression.Compile();
// Method can now be registered
Related
I have an object that provides several functions to write and read data from a packet, something like this:
class Packet
{
void Write(int value) {/*...*/}
//...
int ReadInt() {/*...*/}
bool ReadBool() {/*...*/}
string ReadString() {/*...*/}
Vector3 ReadVector3() {/*...*/}
}
this class stores a byte[] that is sent over the network. If I want to access data previously written, I can do it like this
void MyFunction(Packet packet)
{
int myInt = packet.ReadInt();
bool myBool = packet.ReadBool();
string myString = packet.ReadString();
Vector3 myVector3 = packet.ReadVector3();
//... do stuff
}
I was wondering if there is some syntactic sugar that would allow me to define a function taking a variable number of parameters of different types, detect which dynamic types they are at runtime, call the appropriate function and then return the parameter, something like this:
class Packet
{
//...
void ReadAll(out params object[] objects);
}
void MyFunction(Packet packet)
{
packet.ReadAll(out int myInt, out bool myBool, out string myString, out Vector3 myVector3);
//... do stuff with myInt, myBool, myString, myVector3
}
I looked at params with an object[], the out keyword, generics, and Convert.ChangeType() but I couldn't get anything to work so far. I'm not sure this is even possible, and if it is, if the runtime cost of reflection will highly outweigh the benefits of simpler/less code for something used as frequently as network packets.
Thank you all.
You can try using generics and ValueTuples:
public T Read<T>() where T:ITuple
{
return default(T); // some magic to create and fill one
}
and usage:
var (i, j) = Read<(int, int)>();
// or
var x = Read<(int i, int j)>();
As for reflection - you can cache reflection "results" per type:
public T Read<T>() where T : struct, ITuple
{
return TupleCreator<T>.Create(new ValueReader());
}
static class TupleCreator<T> where T : struct, ITuple
{
private static Func<ValueReader, T> factory;
static TupleCreator()
{
var fieldTypes = typeof(T).GetFields()
.Select(fi => fi.FieldType)
.ToArray();
if(fieldTypes.Length > 7)
{
throw new Exception("TODO");
}
var createMethod = typeof(ValueTuple).GetMethods()
.Where(m => m.Name == "Create" && m.GetParameters().Length == fieldTypes.Length)
.SingleOrDefault() ?? throw new NotSupportedException("ValueTuple.Create method not found.");
var createGenericMethod = createMethod.MakeGenericMethod(fieldTypes);
var par = Expression.Parameter(typeof(ValueReader));
// you will need to figure out how to find your `read` methods
// but it should be easy - just find a method starting with "Read"
// And returning desired type
// I can do as simple as:
var readMethod = typeof(ValueReader).GetMethod("Read");
var readCalls = fieldTypes
.Select(t => readMethod.MakeGenericMethod(t)) // can cache here also but I think not needed to
.Select(m => Expression.Call(par, m));
var create = Expression.Call(createGenericMethod, readCalls);
factory = Expression.Lambda<Func<ValueReader, T>>(create, par).Compile();
}
public static T Create(ValueReader reader) => factory(reader);
}
class ValueReader
{
public T Read<T>()
{
return default; // to simulate your read
}
}
Console.WriteLine(Read<(int i, double j)>()); // prints "(0, 0)"
NB
Also you can just implement the Read<T> method with "caching" the reflection as done here.
Yes, Reflection is always having a cost of Performance, but it's not always so big, especially when you are not using reflective calls via big size collections or dealing with a very complicated code (here is a Microsoft official tutorial about when to use the Reflection).
Anyway, back to our business, using a reflection here is what I think might help you:
class Packet {
...
public T Read<T>()
{
var currentType = Type.GetType(this.GetType().ToString());
var methodInfo = currentType?
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) // Getting all "private" read methods
.FirstOrDefault(m =>
m.Name.Contains("Read") && m.ReturnType == typeof(T));
// here it is assuming that you will have to have only 1 Read method with the type int, bool, etc.
return (T) methodInfo?.Invoke(this, null);
}
}
public List<object> ReadAllAsCollection(params Type[] types)
{
var result = new List<object>(); // in order to hold various type you need to have a collection of `object` type elements
foreach (Type type in types)
{
MethodInfo method = typeof(Packet).GetMethod("Read");
MethodInfo genericMethod = method?.MakeGenericMethod(type);
var res = genericMethod?.Invoke(this, null); // calling Read<T> with appropriate type
result.Add(res);
}
return result;
}
public T ReadAllAsTuple<T>() where T : ITuple
{
T CreateValueTuple(List<object> items, Type[] inputTypes)
{
var methodInfo = typeof(ValueTuple)
.GetMethods()
.FirstOrDefault(m =>
m.Name.Contains("Create") &&
m.GetParameters().Length == items.Count);
var invokeResult = methodInfo?.MakeGenericMethod(inputTypes)
.Invoke(null, items.ToArray());
return (T)invokeResult;
}
var tupleType = typeof(T);
var types = tupleType.GetFields().Select(f => f.FieldType).ToArray();
var result = types
.Select(t =>
{
var method = typeof(Packet).GetMethod("Read");
var genericMethod = method?.MakeGenericMethod(t);
return genericMethod?.Invoke(this, null); // calling Read<T> with appropriate type
}).ToList();
return result.Any() ?
CreateValueTuple(result, types)
: default;
}
And a usage:
var p = new Packet();
var elm = p.Read<int>(); // single instance call, ReadInt() call is encapsulated
var resultAsCollection = p.ReadAllAsCollection(typeof(int), typeof(string));
var resultAsTuple = p.ReadAllAsTuple<ValueTuple<int, string, bool>>();
A few notes:
As you probably want a single exposed public method (Read), it would be good to have other methods declared as private.
In this case, you have simple method names, but to escape possible name conflicts, you can use some specific naming conventions, like: _ReadInt_() or any other namings you prefer. Though, this example will work even without specific namings.
It is assumed that you will have only one method with the name Read... and return type of T (for example int ReadInt()) because, for the above case, we are using only the first match.
Using the new async/await model it's fairly straightforward to generate a Task that is completed when an event fires; you just need to follow this pattern:
public class MyClass
{
public event Action OnCompletion;
}
public static Task FromEvent(MyClass obj)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
obj.OnCompletion += () =>
{
tcs.SetResult(null);
};
return tcs.Task;
}
This then allows:
await FromEvent(new MyClass());
The problem is that you need to create a new FromEvent method for every event in every class that you would like to await on. That could get really large really quick, and it's mostly just boilerplate code anyway.
Ideally I would like to be able to do something like this:
await FromEvent(new MyClass().OnCompletion);
Then I could re-use the same FromEvent method for any event on any instance. I've spent some time trying to create such a method, and there are a number of snags. For the code above it will generate the following error:
The event 'Namespace.MyClass.OnCompletion' can only appear on the left hand side of += or -=
As far as I can tell, there won't ever be a way of passing the event like this through code.
So, the next best thing seemed to be trying to pass the event name as a string:
await FromEvent(new MyClass(), "OnCompletion");
It's not as ideal; you don't get intellisense and would get a runtime error if the event doesn't exist for that type, but it could still be more useful than tons of FromEvent methods.
So it's easy enough to use reflection and GetEvent(eventName) to get the EventInfo object. The next problem is that the delegate of that event isn't known (and needs to be able to vary) at runtime. That makes adding an event handler hard, because we need to dynamically create a method at runtime, matching a given signature (but ignoring all parameters) that accesses a TaskCompletionSource that we already have and sets its result.
Fortunately I found this link which contains instructions on how to do [almost] exactly that via Reflection.Emit. Now the problem is that we need to emit IL, and I have no idea how to access the tcs instance that I have.
Below is the progress that I've made towards finishing this:
public static Task FromEvent<T>(this T obj, string eventName)
{
var tcs = new TaskCompletionSource<object>();
var eventInfo = obj.GetType().GetEvent(eventName);
Type eventDelegate = eventInfo.EventHandlerType;
Type[] parameterTypes = GetDelegateParameterTypes(eventDelegate);
DynamicMethod handler = new DynamicMethod("unnamed", null, parameterTypes);
ILGenerator ilgen = handler.GetILGenerator();
//TODO ilgen.Emit calls go here
Delegate dEmitted = handler.CreateDelegate(eventDelegate);
eventInfo.AddEventHandler(obj, dEmitted);
return tcs.Task;
}
What IL could I possibly emit that would allow me to set the result of the TaskCompletionSource? Or, alternatively, is there another approach to creating a method that returns a Task for any arbitrary event from an arbitrary type?
Here you go:
internal class TaskCompletionSourceHolder
{
private readonly TaskCompletionSource<object[]> m_tcs;
internal object Target { get; set; }
internal EventInfo EventInfo { get; set; }
internal Delegate Delegate { get; set; }
internal TaskCompletionSourceHolder(TaskCompletionSource<object[]> tsc)
{
m_tcs = tsc;
}
private void SetResult(params object[] args)
{
// this method will be called from emitted IL
// so we can set result here, unsubscribe from the event
// or do whatever we want.
// object[] args will contain arguments
// passed to the event handler
m_tcs.SetResult(args);
EventInfo.RemoveEventHandler(Target, Delegate);
}
}
public static class ExtensionMethods
{
private static Dictionary<Type, DynamicMethod> s_emittedHandlers =
new Dictionary<Type, DynamicMethod>();
private static void GetDelegateParameterAndReturnTypes(Type delegateType,
out List<Type> parameterTypes, out Type returnType)
{
if (delegateType.BaseType != typeof(MulticastDelegate))
throw new ArgumentException("delegateType is not a delegate");
MethodInfo invoke = delegateType.GetMethod("Invoke");
if (invoke == null)
throw new ArgumentException("delegateType is not a delegate.");
ParameterInfo[] parameters = invoke.GetParameters();
parameterTypes = new List<Type>(parameters.Length);
for (int i = 0; i < parameters.Length; i++)
parameterTypes.Add(parameters[i].ParameterType);
returnType = invoke.ReturnType;
}
public static Task<object[]> FromEvent<T>(this T obj, string eventName)
{
var tcs = new TaskCompletionSource<object[]>();
var tcsh = new TaskCompletionSourceHolder(tcs);
EventInfo eventInfo = obj.GetType().GetEvent(eventName);
Type eventDelegateType = eventInfo.EventHandlerType;
DynamicMethod handler;
if (!s_emittedHandlers.TryGetValue(eventDelegateType, out handler))
{
Type returnType;
List<Type> parameterTypes;
GetDelegateParameterAndReturnTypes(eventDelegateType,
out parameterTypes, out returnType);
if (returnType != typeof(void))
throw new NotSupportedException();
Type tcshType = tcsh.GetType();
MethodInfo setResultMethodInfo = tcshType.GetMethod(
"SetResult", BindingFlags.NonPublic | BindingFlags.Instance);
// I'm going to create an instance-like method
// so, first argument must an instance itself
// i.e. TaskCompletionSourceHolder *this*
parameterTypes.Insert(0, tcshType);
Type[] parameterTypesAr = parameterTypes.ToArray();
handler = new DynamicMethod("unnamed",
returnType, parameterTypesAr, tcshType);
ILGenerator ilgen = handler.GetILGenerator();
// declare local variable of type object[]
LocalBuilder arr = ilgen.DeclareLocal(typeof(object[]));
// push array's size onto the stack
ilgen.Emit(OpCodes.Ldc_I4, parameterTypesAr.Length - 1);
// create an object array of the given size
ilgen.Emit(OpCodes.Newarr, typeof(object));
// and store it in the local variable
ilgen.Emit(OpCodes.Stloc, arr);
// iterate thru all arguments except the zero one (i.e. *this*)
// and store them to the array
for (int i = 1; i < parameterTypesAr.Length; i++)
{
// push the array onto the stack
ilgen.Emit(OpCodes.Ldloc, arr);
// push the argument's index onto the stack
ilgen.Emit(OpCodes.Ldc_I4, i - 1);
// push the argument onto the stack
ilgen.Emit(OpCodes.Ldarg, i);
// check if it is of a value type
// and perform boxing if necessary
if (parameterTypesAr[i].IsValueType)
ilgen.Emit(OpCodes.Box, parameterTypesAr[i]);
// store the value to the argument's array
ilgen.Emit(OpCodes.Stelem, typeof(object));
}
// load zero-argument (i.e. *this*) onto the stack
ilgen.Emit(OpCodes.Ldarg_0);
// load the array onto the stack
ilgen.Emit(OpCodes.Ldloc, arr);
// call this.SetResult(arr);
ilgen.Emit(OpCodes.Call, setResultMethodInfo);
// and return
ilgen.Emit(OpCodes.Ret);
s_emittedHandlers.Add(eventDelegateType, handler);
}
Delegate dEmitted = handler.CreateDelegate(eventDelegateType, tcsh);
tcsh.Target = obj;
tcsh.EventInfo = eventInfo;
tcsh.Delegate = dEmitted;
eventInfo.AddEventHandler(obj, dEmitted);
return tcs.Task;
}
}
This code will work for almost all events that return void (regardless of the parameter list).
It can be improved to support any return values if necessary.
You can see the difference between Dax's and mine methods below:
static async void Run() {
object[] result = await new MyClass().FromEvent("Fired");
Console.WriteLine(string.Join(", ", result.Select(arg =>
arg.ToString()).ToArray())); // 123, abcd
}
public class MyClass {
public delegate void TwoThings(int x, string y);
public MyClass() {
new Thread(() => {
Thread.Sleep(1000);
Fired(123, "abcd");
}).Start();
}
public event TwoThings Fired;
}
Briefly, my code supports really any kind of delegate type. You shouldn't (and don't need to) specify it explicitly like TaskFromEvent<int, string>.
This will give you what you need without needing to do any ilgen, and way simpler. It works with any kind of event delegates; you just have to create a different handler for each number of parameters in your event delegate. Below are the handlers you'd need for 0..2, which should be the vast majority of your use cases. Extending to 3 and above is a simple copy and paste from the 2-parameter method.
This is also more powerful than the ilgen method because you can use any values created by the event in your async pattern.
// Empty events (Action style)
static Task TaskFromEvent(object target, string eventName) {
var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
var delegateType = addMethod.GetParameters()[0].ParameterType;
var tcs = new TaskCompletionSource<object>();
var resultSetter = (Action)(() => tcs.SetResult(null));
var d = Delegate.CreateDelegate(delegateType, resultSetter, "Invoke");
addMethod.Invoke(target, new object[] { d });
return tcs.Task;
}
// One-value events (Action<T> style)
static Task<T> TaskFromEvent<T>(object target, string eventName) {
var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
var delegateType = addMethod.GetParameters()[0].ParameterType;
var tcs = new TaskCompletionSource<T>();
var resultSetter = (Action<T>)tcs.SetResult;
var d = Delegate.CreateDelegate(delegateType, resultSetter, "Invoke");
addMethod.Invoke(target, new object[] { d });
return tcs.Task;
}
// Two-value events (Action<T1, T2> or EventHandler style)
static Task<Tuple<T1, T2>> TaskFromEvent<T1, T2>(object target, string eventName) {
var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
var delegateType = addMethod.GetParameters()[0].ParameterType;
var tcs = new TaskCompletionSource<Tuple<T1, T2>>();
var resultSetter = (Action<T1, T2>)((t1, t2) => tcs.SetResult(Tuple.Create(t1, t2)));
var d = Delegate.CreateDelegate(delegateType, resultSetter, "Invoke");
addMethod.Invoke(target, new object[] { d });
return tcs.Task;
}
Use would be like this. As you can see, even though the event is defined in a custom delegate, it still works. And you can capture the evented values as a tuple.
static async void Run() {
var result = await TaskFromEvent<int, string>(new MyClass(), "Fired");
Console.WriteLine(result); // (123, "abcd")
}
public class MyClass {
public delegate void TwoThings(int x, string y);
public MyClass() {
new Thread(() => {
Thread.Sleep(1000);
Fired(123, "abcd");
}).Start();
}
public event TwoThings Fired;
}
Here's a helper function that'll allow you to write the TaskFromEvent functions in just one line each, if the above three methods are too much copy-and-paste for your preferences. Credit has to be given to max for simplifying what I had originally.
If you're willing to have one method per delegate type, you can do something like:
Task FromEvent(Action<Action> add)
{
var tcs = new TaskCompletionSource<bool>();
add(() => tcs.SetResult(true));
return tcs.Task;
}
You would use it like:
await FromEvent(x => new MyClass().OnCompletion += x);
Be aware that this way you never unsubscribe from the event, that may or may not be a problem for you.
If you're using generic delegates, one method per each generic type is enough, you don't need one for each concrete type:
Task<T> FromEvent<T>(Action<Action<T>> add)
{
var tcs = new TaskCompletionSource<T>();
add(x => tcs.SetResult(x));
return tcs.Task;
}
Although type inference doesn't work with that, you have to explicitly specify the type parameter (assuming the type of OnCompletion is Action<string> here):
string s = await FromEvent<string>(x => c.OnCompletion += x);
I faced this problem by trying to write GetAwaiter extension method for System.Action, forgetting that System.Action is immutable and by passing it as an argument you make a copy. However, you do not make a copy if you pass it with ref keyword, thus:
public static class AwaitExtensions
{
public static Task FromEvent(ref Action action)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
action += () => tcs.SetResult(null);
return tcs.Task;
}
}
Usage:
await AwaitExtensions.FromEvent(ref OnActionFinished);
Note: TCS listener remain subscribed
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,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.
I am using C#/.NET 4.0 and a Protocol Buffers library (protobuf-net) which provides the following functionality.
public static class Serializer {
public static void Serialize<T>(Stream destination, T instance);
public static void Serialize<T>(SerializationInfo info, T instance);
public static void Serialize<T>(XmlWriter writer, T instance);
public static void Serialize<T>(SerializationInfo info, StreamingContext context, T instance);
public static T Deserialize<T>(Stream source);
}
I need to wrap two of these calls with non-generic equivalents. Specifically, I want
void SerializeReflection(Stream destination, object instance);
object DeserializeReflection(Stream source, Type type);
that simply call the respective generic members of Serializer at runtime. I have gotten the DeserializeReflection method to work with the following code:
public static object DeserializeReflection(Stream stream, Type type)
{
return typeof(Serializer)
.GetMethod("Deserialize")
.MakeGenericMethod(type)
.Invoke(null, new object[] { stream });
}
The SerializeReflection method is what is causing me trouble. I at first tried the following code:
public static void SerializeReflection(Stream stream, object instance)
{
typeof(Serializer)
.GetMethod("Serialize")
.MakeGenericMethod(instance.GetType())
.Invoke(null, new object[] { stream, instance });
}
The problem is that the part between typeof(Serializer) and .Invoke(...) is not working. The call to GetMethod("Serialize") gets me an AmbiguousMatchException, because there are four methods named "Serialize."
I then tried using the overload of GetMethod that takes an array of System.Type to resolve the binding:
GetMethod("Serialize", new[] { typeof(Stream), instance.GetType() })
But this just made the result of GetMethod null.
How can I use reflection to get the MethodInfo for void Serializer.Serialize<T>(Stream, T), where T is instance.GetType()?
Try to use next code snippet to see if it meets your need. It creates a close typed instance of method public static void Serialize<T>(Stream destination, T instance). In this case it select the first method with Stream as parameter, but you can change this predicate method.GetParameters().Any(par => par.ParameterType == typeof(Stream)) to whatever you want
public static object DeserializeReflection(Stream stream, object instance)
{
return typeof(Serializer)
.GetMethods()
.First(method => method.Name == "Serialize" && method.GetParameters().Any(par => par.ParameterType == typeof(Stream)))
.MakeGenericMethod(instance.GetType())
.Invoke(null, new object[] { stream, instance });
}
For this sort of thing I often user helper methods like this
public static MethodInfo MakeGenericMethod<TSourceType>(Type genericArgument, string methodName, Type[] parameterTypes, params int[] indexesWhereParameterIsTheGenericArgument)
{
//Get the type of the thing we're looking for the method on
var sourceType = typeof (TSourceType);
//Get all the methods that match the default binding flags
var allMethods = sourceType.GetMethods();
//Get all the methods with the same names
var candidates = allMethods.Where(x => x.Name == methodName);
//Find the appropriate method from the set of candidates
foreach (var candidate in candidates)
{
//Look for methods with the same number of parameters and same types
// of parameters (excepting for ones that have been marked as
// replaceable by the generic parameter)
var parameters = candidate.GetParameters();
var successfulMatch = parameters.Length == parameterTypes.Length;
if (successfulMatch)
{
for (var i = 0; i < parameters.Length; ++i)
{
successfulMatch &= parameterTypes[i] == parameters[i].ParameterType || indexesWhereParameterIsTheGenericArgument.Contains(i);
}
}
//If all the parameters were validated, make the generic method and return it
if (successfulMatch)
{
return candidate.MakeGenericMethod(genericArgument);
}
}
//We couldn't find a suitable candidate, return null
return null;
}
To use it, you'd do
var serializeMethod = MakeGenericMethod<Serializer>(instance.GetType(), "Serialize", new[]{typeof(stream), typeof(object)}, 1);