How do I call DynamicInvoke on an Expression<Action<T>> using Compile? - c#

Why doesn't this work?
namespace InvokeTest
{
public class MethodInvoker
{
public static void Execute<T>(Expression<Action<T>> call)
{
// Parameter count mismatch
call.Compile().DynamicInvoke();
// Also attempted this:
//call.Compile().DynamicInvoke(1);
// but it threw: "Object of type 'System.Int32' cannot be converted to type 'InvokeTest.TestClass'."
}
}
public class TestClass
{
public TestClass()
{ }
public void DoSomething(int num)
{
System.Console.WriteLine("It works");
}
public void KickOff()
{
MethodInvoker.Execute<TestClass>(m => m.DoSomething(1));
}
}
}

An Action<T> is the same as a delegate void F<T>(T t). That is, an Action<T> is an method that has return type void and consumes instances of T.
When you call
MethodInvoker.Execute<TestClass>(m => m.DoSomething(1));
you have set the type parameter T to be TestClass. Therefore, you need to pass in a parameter, and that parameter has to be instance of TestClass.
This is why in the first case you get a parameter count mismatch, and in the second case the compiler wants to convert the parameter to an instance of TestClass. You need to pass one parameter, and that parameter needs to be an instance of TestClass.
Note that your action is
m => m.DoSomething(1).
So your action takes an instance of TestClass which you are parameterizing by m and invokes m.DoSomething(1) on that instance. Now, when you dynamically invoke this method, you need to be giving it an instance of TestClass. You aren't doing that.

Since you defined the parameter type
Expression<Action<T>>
the function will require expression in accordance with the following delegate:
Action<TestClass>
which is:
public void SampleMethod(TestClass a)
Based on this the correct invocation should look like this:
var t = new TestClass();
call.Compile().DynamicInvoke(t);

Related

MongoDB, C#, Reflection, Dynamic Execution [duplicate]

Here's my class:
public class MyClass<T>
{
public void MyMethod(T a)
{
}
public void MyMethod(int a)
{
}
}
How would I Invoke MyMethod(T) using Reflection even if int is the generic type parameter for MyClass?
Here's a single statement that does it (Might be inefficient, but I like brevity):
var mc = new MyClass<int>();
typeof(MyClass<int>).GetMethods().ElementAt(
typeof(MyClass<int>).
GetGenericTypeDefinition().
GetMethods().ToList().FindIndex(m =>
m.Name.Equals("MyMethod") &&
m.GetParameters().Count() == 1 &&
m.GetParameters()[0].ParameterType.IsGenericParameter)
).Invoke(mc, new object[] { 1 });
Revised Answer
Okay, so I think I've figured out why the IsGenericParameter property evaluates as false is because you were creating the MyClass type with an explicit type for <T>.
Since the compiler new what type the a parameter was (inferring from the instantiation of the class), I'm guessing that the compiler was treating the parameter as a non-generic type.
Also, based upon what I was reading in MSDN, I think that the ParameterType.IsGenericParameter and Type.IsGenericType property would only evaluate to true
whenever you had a method like MyMethod<T>() vs. MyMethod(T a), where the type for <T> is inferred from the type instantiated with the class.
Here's a little program that demonstrates this:
using System;
using System.Linq;
using System.Reflection;
namespace GenericParametersViaReflectionTest
{
class Program
{
static void Main(string[] args)
{
// Note: we're using the type without specifying a type for <T>.
var classType = typeof(MyClass<>);
foreach (MethodInfo method in classType.GetMembers()
.Where(method => method.Name == "MyMethod"))
{
// Iterate through each parameter of the method
foreach (var param in method.GetParameters())
{
// For generic methods, the name will be "T" and the FullName
// will be null; you can use which ever check you prefer.
if (param.ParameterType.Name == "T"
|| param.ParameterType.FullName == null)
Console.WriteLine("We found our generic method!");
else
Console.WriteLine("We found the non-generic method:");
Console.WriteLine("Method Name: {0}", method.Name);
Console.WriteLine("Parameter Name: {0}", param.Name);
Console.WriteLine("Type: {0}", param.ParameterType.Name);
Console.WriteLine("Type Full Name: {0}",
param.ParameterType.FullName ?? "null");
Console.WriteLine("");
}
}
Console.Read();
}
}
public class MyClass<T>
{
public void MyMethod(T a) { }
public void MyMethod(int a) { }
}
}
And the results we end up with are:
We found or generic method!
Method Name: MyMethod
Parameter Name: a
Type: T
Type Full Name: null
We found the non-generic method:
Method Name: MyMethod
Parameter Name: a
Type: Int32
Type Full Name: System.Int32
If you need to create an instance of the class using a particular type, you might find the Activator.CreateInstance class useful too.
Original Answer
I think that if you pass in a parameter of the same datatype that matched one of the explictly set methods (e.g. the Int32 method), then the compiler would automatically select that over the one that accepts a generic parameter. Otherwise, the generic method would be selected by the compiler.
However, if you want to be able to control which method is selected, you could modify each method to have different parameter names, while maintaining identical signatures, like so:
public class MyClass<T>
{
public void MyMethod(T genericA) {}
public void MyMethod(int intA) {}
}
Then, using named parameters, you could explicitly call the desired method, like so:
var foo = new MyClass<int>();
foo.MyMethod(genericA: 24); // This will call the "MyMethod" that only accepts <T>.
foo.MyMethod(intA: 19); // This will call the "MyMethod" that only accepts integers.
Edit
For some reason, in my original answer I missed the part where you mentioned using reflection, but it looks my original answer could be coupled with these other answers to give you a viable solution:
Invoking methods with optional parameters through reflection
How can you get the names of method parameters?
The only way I was able to do this was to use GetGenericTypeDefinition with IsGenericParameter. In the generic type definition, one method will have IsGenericParameter set to true on the parameter. However, for closed types none of the parameters will have this as true. Then, you can't use the MethodInfo from the generic type definition to invoke the method, so I stored the index and used that to lookup the corresponding MethodInfo in the closed type.
public class Program
{
public static void Main(string[] args)
{
bool invokeGeneric = true;
MyClass<int> b = new MyClass<int>();
var type = b.GetType().GetGenericTypeDefinition();
int index = 0;
foreach(var mi in type.GetMethods().Where(mi => mi.Name == "MyMethod"))
{
if (mi.GetParameters()[0].ParameterType.IsGenericParameter == invokeGeneric)
{
break;
}
index++;
}
var method = b.GetType().GetMethods().Where(mi => mi.Name == "MyMethod").ElementAt(index);
method.Invoke(b, new object[] { 1 });
}
}
public class MyClass<T>
{
public void MyMethod(T a)
{
Console.WriteLine("In generic method");
}
public void MyMethod(int a)
{
Console.WriteLine("In non-generic method");
}
}

Can I find a method with generic parameters using strongly typed reflection?

Here's my attempt at calling an existing generic method with a type argument. 'Strongly typed reflection' may not be a suitable term, but it basically means finding and invoking the reflected method without using a name string.
public class TestClass
{
public static void Test(Type type)
{
InvokeTestMethodWithType(type);
}
private void Test<T>() { ... }
private static void InvokeTestMethodWithType(Type type)
{
// This doesn't compile! - can I find Test<> using this approach?
Expression<Func<TestClass, Action>> ex = x => x.Test<>;
// invoke it
((MethodCallExpression)ex.Body).Method.MakeGenericMethod(type).Invoke(new TestClass(), null);
}
}
Sample call would end up call the private Test().
TestClass.Test(typeof(Foo))
As you can see, I'm struggling with the expression and not entirely sure if it can be executed in this manner.
Do I have to dummy invoke the action in the expression like this post?
x => x.Test<object>()
The trick I use is simple: pass a fake generic type argument:
Expression<Func<TestClass, WhateverTestReturns>> ex = x => x.Test<string>();
// invoke it
((MethodCallExpression)ex.Body)
.Method
.GetGenericMethodDefinition()
.MakeGenericMethod(type)
.Invoke(new TestClass(), null);
The method call expression will then contain a method info for Test<string>(), but you can easily use GetGenericMethodDefinition to remove the generic argument, and then MakeGenericMethod to put a different one back in its place.
You don't even need to use Expression in a case like this - simply cast TestClass.Test<string> to a delegate, and you'll have a Method property that gives you the same method info.

Lambda not a property type when passing property

I have the following class:
public class Errors<T> : Errors
{
public void Add(Expression<Func<T>> property, string message)
{
base.Add(((MemberExpression)property.Body).Member.Name, message);
}
}
Which I then try and invoke like this:
Errors<User> e = new Errors<User>();
e.Add(x => x.Name, "Name must be entered.");
When I attempt to compile, I get the following error:
Cannot convert lambda expression to type 'string' because it is not a delegate type
Where is my definition wrong? The error occurs on the e.Add method call, not on the overload.
You've specified Func<T> in your overload, which should take no argument and return T (in this case, User). You're passing a lambda which looks more like Func<T, object> - it accepts a T parameter and returns something.
I imagine your Errors base class has a function like this:
public class Errors{
public void Add(string propertyName, string message) {
// implementation here
}
}
Which is what the error is talking about. It's trying to match your lambda to the parameters of that overload, because it doesn't match the Func<T> you specified in your generic class's overload.
So, I think your overload should be:
public void Add(Expression<Func<T, object>> property, string message)

Call generic method by type

I have these classes:
public static class A{
...
public C Run<T>(string something)
{
...
}
}
public static class B{
...
public void Exec<T>(Type type)
{
MethodInfo method = typeof(A).GetMethod("Run");
MethodInfo generic = method.MakeGenericMethod(type);
var result = generic.Invoke(null, new object[] { "just a string" });
// bad call in next line
result.DoSomething();
}
}
public class C{
...
public void DoSomething(){}
}
How to convert result to type for call DoSomething method? And how simpler to call generic method using type variable?
How to convert result to type for call DoSomething method?
You cannot do that statically, because your code does not know the type at compile time, and the object is already of the correct type. One way to do it in .NET 4.0 and later is to use dynamic instead of object for the result, like this:
dynamic result = generic.Invoke(null, new object[] { "just a string" });
result.DoSomething(); // This will compile
You can do it like this only if you are 100% certain that the DoSomething() method is going to be there at runtime. Otherwise, there is going to be an exception at runtime, which you would need to catch and process.

Setting a generic type parameter from a method parameter

Is there any way to do something like this?
void SomeMethod(Type generic)
{
SomeGenericMethod<generic>();
}
I need to pass the type in as a "normal" parameter and not a generic type parameter.
You can do it with reflection:
public class foo
{
public void SomeMethod(Type type)
{
var methodInfo = this.GetType().GetMethod("SomeGenericMethod");
var method = methodInfo.MakeGenericMethod(new[] { type });
method.Invoke(this, null);
}
public void SomeGenericMethod<T>()
{
Debug.WriteLine(typeof(T).FullName);
}
}
class Program
{
static void Main(string[] args)
{
var foo = new foo();
foo.SomeMethod(typeof(string));
foo.SomeMethod(typeof(foo));
}
}
That said, using reflection in this way means you are losing some of the benefits of using generics in the first place, so you might want to look at other design alternatives.
Assuming that your method is defined in a class called MyClass, this should do it:
var MyObject = new MyClass();
typeof(MyClass).GetMethod("SomeGenericMethod").MakeGenericMethod(generic).Invoke(myObject, null);
Type.GetMethod() gets an object which describes a method defined in the Type it's called on.
The method is generic, so we need to call MakeGenericMethod, and pass its one generic parameter.
We then invoke the method, passing the object we want the method called on and any arguments it takes. As it takes no arguments, we just pass null.

Categories