How do I call SomeObject.SomeGenericInstanceMethod<T>(T arg) ?
There are a few posts about calling generic methods, but not quite like this one. The problem is that the method argument parameter is constrained to the generic parameter.
I know that if the signature were instead
SomeObject.SomeGenericInstanceMethod<T>(string arg)
then I could get the MethodInfo with
typeof (SomeObject).GetMethod("SomeGenericInstanceMethod", new Type[]{typeof (string)}).MakeGenericMethod(typeof(GenericParameter))
So, How do I go about getting the MethodInfo when the regular arguments are of a generic type? Thanks!
Also, there may or may not be type constrains on the generic parameter.
You do it exactly the same way.
When you call MethodInfo.Invoke, you pass all the arguments in an object[] anyway, so it's not like you have to know the types at compile time.
Sample:
using System;
using System.Reflection;
class Test
{
public static void Foo<T>(T item)
{
Console.WriteLine("{0}: {1}", typeof(T), item);
}
static void CallByReflection(string name, Type typeArg,
object value)
{
// Just for simplicity, assume it's public etc
MethodInfo method = typeof(Test).GetMethod(name);
MethodInfo generic = method.MakeGenericMethod(typeArg);
generic.Invoke(null, new object[] { value });
}
static void Main()
{
CallByReflection("Foo", typeof(object), "actually a string");
CallByReflection("Foo", typeof(string), "still a string");
// This would throw an exception
// CallByReflection("Foo", typeof(int), "oops");
}
}
You do it exactly the same way, but pass an instance of your object:
typeof (SomeObject).GetMethod(
"SomeGenericInstanceMethod",
yourObject.GetType())
// Or typeof(TheClass),
// or typeof(T) if you're in a generic method
.MakeGenericMethod(typeof(GenericParameter))
The MakeGenericMethod method only requires you to specify the generic type parameters, not the method's arguments.
You'd pass the arguments in later, when you call the method. However, at this point, they're passing as object, so it again doesn't matter.
Related
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.
Currently I am trying to make an Expression Tree with MethodCallExpressions for When, Select and GroupBy. I started with this manual on MSDN and several posts on StackOverflow. This gives us good examples to get started. To be able to write my own Expression Trees, without examples, I feel the need to understand the Expression.Call method with parameters as follows:
public static MethodCallExpression Call(
Expression instance,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
The third parameter is described by Microsoft like this:
An array of Type objects that specify the type parameters of the generic method. This argument should be null when methodName specifies a non-generic method.
This sounds rather general to me and I can't find out how to define which Type(s) I should pass with my Expression.Call method call.
Who can help me with a general explanation of the typeArguments parameter? Thanks in advance.
The typeArguments parameter is used to call a generic method without giving the MethodInfo. Consider these functions for example:
public void NonGenericMethod()
{
}
public void GenericMethod<T>()
{
}
public void GenericMethod2<T1, T2>()
{
}
For NonGenericMethod the typeArguments should be an empty array (use Type.EmptyTypes). GenericMethod<T> has 1 type argument: the typeArguments should be an array with one Type (if you wanted to call GenericMethod<int>, it would be new [] { typeof(int) }). GenericMethod2<T1, T2> has 2 type arguments: the typeArguments array should have two elements (GenericMethod2<int, int> would be new [] { typeof(int), typeof(int) } ). And so on.
A generic method is a method that has one or more type parameters. You create a generic method to apply an algorithm over a variety of types. For example:
public T DoSomething<T>(T input)
{
return T; // do something useful
}
You can call this method like this:
var result = DoSomething<int>(123);
In order to call a generic method, you have to specify the Type parameters. Therefore, you use the typeArguments argument in the Expression.Call method.
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.
How do I call SomeObject.SomeGenericInstanceMethod<T>(T arg) ?
There are a few posts about calling generic methods, but not quite like this one. The problem is that the method argument parameter is constrained to the generic parameter.
I know that if the signature were instead
SomeObject.SomeGenericInstanceMethod<T>(string arg)
then I could get the MethodInfo with
typeof (SomeObject).GetMethod("SomeGenericInstanceMethod", new Type[]{typeof (string)}).MakeGenericMethod(typeof(GenericParameter))
So, How do I go about getting the MethodInfo when the regular arguments are of a generic type? Thanks!
Also, there may or may not be type constrains on the generic parameter.
You do it exactly the same way.
When you call MethodInfo.Invoke, you pass all the arguments in an object[] anyway, so it's not like you have to know the types at compile time.
Sample:
using System;
using System.Reflection;
class Test
{
public static void Foo<T>(T item)
{
Console.WriteLine("{0}: {1}", typeof(T), item);
}
static void CallByReflection(string name, Type typeArg,
object value)
{
// Just for simplicity, assume it's public etc
MethodInfo method = typeof(Test).GetMethod(name);
MethodInfo generic = method.MakeGenericMethod(typeArg);
generic.Invoke(null, new object[] { value });
}
static void Main()
{
CallByReflection("Foo", typeof(object), "actually a string");
CallByReflection("Foo", typeof(string), "still a string");
// This would throw an exception
// CallByReflection("Foo", typeof(int), "oops");
}
}
You do it exactly the same way, but pass an instance of your object:
typeof (SomeObject).GetMethod(
"SomeGenericInstanceMethod",
yourObject.GetType())
// Or typeof(TheClass),
// or typeof(T) if you're in a generic method
.MakeGenericMethod(typeof(GenericParameter))
The MakeGenericMethod method only requires you to specify the generic type parameters, not the method's arguments.
You'd pass the arguments in later, when you call the method. However, at this point, they're passing as object, so it again doesn't matter.
This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 9 years ago.
I want to call my generic method with a given type object.
void Foo(Type t)
{
MyGenericMethod<t>();
}
obviously doesn't work.
How can I make it work?
Your code sample won't work, because the generic method expects a type identifier, not a an instance of the Type class. You'll have to use reflection to do it:
public class Example {
public void CallingTest()
{
MethodInfo method = typeof (Example).GetMethod("Test");
MethodInfo genericMethod = method.MakeGenericMethod(typeof (string));
genericMethod.Invoke(this, null);
}
public void Test<T>()
{
Console.WriteLine(typeof (T).Name);
}
}
Do keep in mind that this is very brittle, I'd rather suggest finding another pattern to call your method.
Another hacky solution (maybe someone can make it a bit cleaner) would be to use some expression magic:
public class Example {
public void CallingTest()
{
MethodInfo method = GetMethod<Example>(x => x.Test<object>());
MethodInfo genericMethod = method.MakeGenericMethod(typeof (string));
genericMethod.Invoke(this, null);
}
public static MethodInfo GetMethod<T>(Expression<Action<T>> expr)
{
return ((MethodCallExpression) expr.Body)
.Method
.GetGenericMethodDefinition();
}
public void Test<T>()
{
Console.WriteLine(typeof (T).Name);
}
}
Note passing the 'object' type identifier as a generic type argument in the lambda. Couldn't figure out so quickly how to get around that. Either way, this is compile-time safe I think. It just feels wrong somehow :/
You need to use reflection, unfortunately (for the reasons Jared mentioned). For example:
MethodInfo method = typeof(Foo).GetMethod("MyGenericMethod");
method = method.MakeGenericMethod(t);
method.Invoke(this, new object[0]);
Obviously you'd want more error checking in reality :)
Side note: my local MSDN doesn't specify that the parameter from MakeGenericMethod is a parameter array, so I'd have expected to require:
method = method.MakeGenericMethod(new Type[] { t });
but it seems it is a parameter array in reality, and the online MSDN docs agree. Odd.
This approach will not work. The reason why is that Type is an object who's type is determined at runtime. However you are trying to use it to call a generic method. A generic method call's type is established at compile time. Hence a Type object can't ever be used for a type parameter on a generic method.