Suppose I have a class like this, containing a generic method with an out parameter:
public class C
{
public static void M<T>(IEnumerable<T> sequence, out T result)
{
Console.WriteLine("Test");
result = default(T);
}
}
From reading the answers to a couple of other questions (How to use reflection to call generic Method? and Reflection on a static overloaded method using an out parameter), I thought I might be able to invoke the method via reflection as follows:
// get the method
var types = new[] { typeof(IEnumerable<int>), typeof(int).MakeByRefType() };
MethodInfo mi = typeof(C).GetMethod(
"M", BindingFlags.Static, Type.DefaultBinder, types, null);
// convert it to a generic method
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
// call it
var parameters = new object[] { new[] { 1 }, null };
generic.Invoke(null, parameters);
But mi is coming back null. I've tried using object instead of int in the types array but that doesn't work either.
How can I specify the types (needed for the out parameter) for a generic method before the call to MakeGenericMethod?
This will let you call the method:
MethodInfo mi = typeof(C).GetMethod("M");
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
var parameters = new object[] { new[]{1},null};
generic.Invoke(null, parameters);
And to get the out parameter:
Console.WriteLine((int)parameters[1]); //will get you 0(default(int)).
I'm still interested to know what the syntax is for specifying an array of template types, or if it's not possible
I don't think it's possible to pass that kind of detailed type specification to GetMethod[s]. I think if you have a number of such Ms to look through, you have to get them all and then filter by the various properties of the MethodInfos and contained objects, eg as much of this as is necessary in your particular case:
var myMethodM =
// Get all the M methods
from mi in typeof(C).GetMethods()
where mi.Name == "M"
// that are generic with one type parameter
where mi.IsGenericMethod
where mi.GetGenericArguments().Length == 1
let methodTypeParameter = mi.GetGenericArguments()[0]
// that have two formal parameters
let ps = mi.GetParameters()
where ps.Length == 2
// the first of which is IEnumerable<the method type parameter>
where ps[0].ParameterType.IsGenericType
where ps[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
where ps[0].ParameterType.GetGenericArguments()[0] == methodTypeParameter
// the second of which is ref <the method type parameter>
where ps[1].ParameterType.IsByRef
where ps[1].ParameterType.GetElementType() == methodTypeParameter
select mi;
You've passed parameters that will find M<T>(IEnumerable<int>, ref int).
You need to find M(IEnumerable<T>, ref T) (the distinction between ref and out exists only in the C# language; reflection only has ref).
I'm not sure how to pass that; you may need to loop through all methods to find it.
On an unrelated note, you need to pass more BindingFlags:
BindingFlags.Public | BindingFlags.Static
This is a well known-problem; to find the method, you need to know its type parameter, but you can't know its type parameter without knowing the method first...
An obvious but inelegant solution is to loop through all methods until you find the right one.
Another option is to take advantage of the Linq Expression API:
public static MethodInfo GetMethod(Expression<Action> expr)
{
var methodCall = expr.Body as MethodCallExpression;
if (methodCall == null)
throw new ArgumentException("Expression body must be a method call expression");
return methodCall.Method;
}
...
int dummy;
MethodInfo mi = GetMethod(() => C.M<int>(null, out dummy));
Related
I need to call the OrderBy<T, TKey>(Func<T, TKey>) method with a value for TKey only available at runtime. After reading answers on SO on how to use a variable as a generic parameter, I'm trying the following approach:
string key = "MyProperty";
Type keyType = typeof(T).GetProperty(key).PropertyType;
MethodInfo methodInfo = typeof(MyClass)
.GetMethod(
"MyGenericStaticMethod"),
BindingFlags.NonPublic | BindingFlags.Static);
// T is known at compile time.
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(new[] { typeof(T), keyType});
var expression = genericMethodInfo.Invoke(null, new object[] { params });
myQueryable.OrderBy(expression);
The problem is, genericMethodInfo.Invoke() returns object and hence cannot be used with OrderBy() which expects an argument of type Func<T, TKey>. However, TKey can be different value types like string,int that are only known at runtime. Can this even be done and if so, how?
Method MethodInfo.Invoke() is used to perform method call with provided parameters, and cannot be used to generate expression. To generate lambda Expression that can be used as an argument to .OrderBy() method, use this instead:
string key = "MyProperty";
Type keyType = typeof(T).GetProperty(key).PropertyType;
MethodInfo methodInfo = typeof(MyClass)
.GetMethod(
"MyGenericStaticMethod",
BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(new[] { typeof(T), keyType });
//this represents parameter of keySelector expression used in OrderBy method
var parameterExpression = Expression.Parameter(typeof(T));
// Expression representing call to MyGenericStaticMethod
var expression = Expression.Call(genericMethodInfo, parameterExpression);
// To use it as an argument of OrderBy method, we must convert expression to lambda
var lambda = Expression.Lambda(expression, parameterExpression);
You will probably encounter another problem: You cannot simply call myQueryable.OrderBy(lambda), because this doesn't allow compiller to infer it's generic arguments and you cannot provide these generic arguments, because TKey is not known at compile time. So you will need to do another reflection, to actually call an .OrderBy() method:
// OrderBy method has generic parameters and several overloads. It is thus
// difficult to get it's MethodInfo just by typeof(Queryable).GetMethod().
// Although it may seem weird, but it is easier to get it's MethodInfo from
// some arbitrary expression. Generic arguments "<object, object>" does not
// matter for now, we will replace them later
Expression<Func<IQueryable<object>, IQueryable<object>>> orderByExpression =
x => x.OrderBy<object, object>((o) => null);
// Replace generic parameters of OrderBy method with actual generic arguments
var orderByMethodInfo = (orderByExpression.Body as MethodCallExpression)
.Method
.GetGenericMethodDefinition()
.MakeGenericMethod(new[] { typeof(T), keyType });
// Now we are finally ready to call OrderBy method
var orderedResultQuery = orderByMethodInfo.Invoke(
null,
new Object[] { myQueryable, lambda })
as IQueryable<T>;
// Just for testing purpose, let's materialize result to list
var orderedResult = orderedResultQuery.ToList();
I am trying to invoke a generic methods that accepts a single params parameter through reflection.
When I picked it to be non generic passing an object[] item seemed to be sufficient but when I reqired to call a generic method it does not work anymore.
var type = typeof (ClassWithGenericMethod);
var method = type.GetMethod("GenericMethod", BindingFlags.Instance | BindingFlags.Public);
var genericMethod = method.MakeGenericMethod(typeof(object));
var result = (bool)genericMethod.Invoke(new ClassWithGenericMethod(), new object[]{"param"});
Assert.IsTrue(result);
The called class:
public class ClassWithGenericMethod
{
public bool GenericMethod<T>(params string[] input)
{
return input.Length == 1;
}
}
The code fails before the assert with the following exception:
Object of type 'System.String' cannot be converted to type
'System.String[]'.
Try repleace new object[]{"param"} with new object[] { new[] { "param" } }.
Here new object[] is the array of parameters, and the first parameter should be a string[], but in your code, you use a string, hence the exception.
When using reflection to call a method that has a params keyword specified, you should just ignore the params keyword. You will need to specify an array of the appropriate type and pass that as the argument.
In your case, instead of passing a single parameter of "param", you need to pass an array of string containing a single item.
The params keyword actually affects how the method caller is compiled. When the compiler sees that the called method specifies "params", it builds the appropriate type of array and passes it. The compiler "magically" turns the following
string result = string.Concat("A","B","C","D");
into basically a compiled version of the following
string[] x = {"A", "B", "C", "D"};
string result = string.Concat(x);
EDIT: the class/method that i'm trying to run this inside is static and therefore i'm unable to pass this into the generic.Invoke
I have a static Data Access Class that i use to automatically parse data from various sources.
i was starting to re-factor it when i ran into a problem.
Im tring to pass a Type to a Generic method via reflection,
(the method then parses the type and returns the Type with a value)
my code currently looks like
Type type1 = typeof( T );
var item = (T)Activator.CreateInstance( typeof( T ), new object[] { } );
foreach (PropertyInfo info in type1.GetProperties())
{
Type dataType = info.PropertyType;
Type dataType = info.PropertyType;
MethodInfo method = typeof( DataReader ).GetMethod( "Read" );
MethodInfo generic = method.MakeGenericMethod( dataType );
//The next line is causing and error as it expects a 'this' to be passed to it
//but i cannot as i'm inside a static class
generic.Invoke( this, info.Name, reader );
info.SetValue(item,DataReader.Read<dataType>(info.Name, reader ) , null);
}
I guess DataReader.Read is the static method, right?
Therefore, change the error line like below, since you are calling the static method. There is not object, so you just pass null into Invoke method:
var value = generic.Invoke( null, new object[] {info.Name, reader} );
The type parameter to a generic method isn't an instance of Type; you can't use your variable in this way. However, you can use reflection to create the closed-generic MethodInfo you require (that is, with the type parameter specified), which would look something like this:
// this line may need adjusting depending on whether the method you're calling is static
MethodInfo readMethod = typeof(DataReader).GetMethod("Read");
foreach (PropertyInfo info in type1.GetProperties())
{
// get a "closed" instance of the generic method using the required type
MethodInfo genericReadMethod m.MakeGenericMethod(new Type[] { info.PropertyType });
// invoke the generic method
object value = genericReadMethod.Invoke(info.Name, reader);
info.SetValue(item, value, null);
}
This is best explained using code. I have a generic class that has a method that returns an integer. Here is a simple version for the purposes of explaining...
public class Gen<T>
{
public int DoSomething(T instance)
{
// Real code does something more interesting!
return 1;
}
}
At runtime I use reflection to discover the type of something and then want to create an instance of my Gen class for that specific type. That is easy enough and done like this...
Type fieldType = // This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
I now want to create an Expression that will take as a parameter an instance of the generic type and then calls the DoSomething method of that type. So I want the Expression to effectively perform this...
int answer = genericInstance.DoSomething(instance);
...except I do not have the 'instance' until some point later at runtime and the genericInstance is the generated type as can be seen above. My attempt at creating the Lambda for this is as follows...
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var x = Expression.Lambda<Func<genericType, fieldType, int>>
(Expression.Call(p1, mi, p2),
new[] { p1, p2 }).Compile();
...so that later on I can call it with something like this...
int answer = x(genericInstance, instance);
Of course, you cannot provide Func with instance parameters and so I have no idea how to parameterize the Lambda generation. Any ideas?
I think you would just use the Expression.Lambda that takes the delegate type as a type rather then as a generic, and create your Func on the fly like you are with Gen<>:
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var func = typeof (Func<,,>);
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int));
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2),
new[] { p1, p2 }).Compile();
This will return a Delegate rather than a strongly typed Func, but you can of course cast it if needed (and seemingly difficult if you don't know what you are casting to), or dynamically invoke it using DynamicInvoke on it.
int answer = (int) x.DynamicInvoke(genericInstance, instance);
EDIT:
A good idea that does indeed work. Unfortunately the reason I want to use a strongly typed compiled Lambda is performance. Using DynamicInvoke is prettty slow compared to a typed Lambda.
This seems to work without the need of a dynamic invoke.
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var func = typeof(Func<,,>);
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int));
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), new[] { p1, p2 });
var invoke = Expression.Invoke(x, Expression.Constant(genericInstance), Expression.Constant(instance));
var answer = Expression.Lambda<Func<int>>(invoke).Compile()();
EDIT 2:
A greatly simplified version:
Type fieldType = ;// This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var value = Expression.Constant(instance, fieldType);
var lambda = Expression.Lambda<Func<int>>(Expression.Call(Expression.Constant(genericInstance), mi, value));
var answer = lambda.Compile()();
This answer only applies if you are using .NET 4.0.
If you make genericInstance dynamic instead of object, you can then call the DoSomething method on it directly, and the dynamic language runtime will take care of everything for you.
class Type1 {
public int DoSomething() { return 1; }
}
class Type2 {
public int DoSomething() { return 2; }
}
static void TestDynamic() {
dynamic t1 = Activator.CreateInstance(typeof(Type1));
int answer1 = t1.DoSomething(); // returns 1
dynamic t2 = Activator.CreateInstance(typeof(Type2));
int answer2 = t2.DoSomething(); // returns 2
}
If you need to keep this class structure (Gen<T>), then I don't see an easy way around the fact that you don't know the type T at compile time. If you want to call the delegate, you either have to know its full type at compile time, or you need to pass in the parameters as objects.
Using dynamic gets you to hide the complexity of getting the MethodInfo, etc., and gives you excellent performance. The one drawback vs. DynamicInvoke that I see is that I believe you get the initial overhead of resolving the dynamic call once for every call site. The bindings are cached so that they run very fast from the second time onwards if you call it on objects with the same type.
It's better to to accept an object and use convert to a known type.
Here is an example, how to build access to a property by name on unknown depth:
var model = new { A = new { B = 10L } };
string prop = "A.B";
var parameter = Expression.Parameter(typeof(object));
Func<object, long> expr = (Func<object, long>) Expression.Lambda(prop.Split('.').Aggregate<string, Expression>(Expression.Convert(parameter, model.GetType()), Expression.Property), parameter).Compile();
expr(model).Dump();
It avoids extra costs of DynamicInvoke when type of delegate is unknown at compile time.
I have a bunch of IEnumerable Collections which exact number and types is subject of frequent changes (due to automatic code generation).
It looks something like this:
public class MyCollections {
public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection;
public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection;
...
At runtime i want to determine each Type and it's count without having to rewrite the code after every code generation. So i am looking for a generic approach using reflection. The result i am looking for is something like:
MyType: 23
OtherType: 42
My problem is that i can't figure how to invoke the Count method properly. Here is what i have so far:
// Handle to the Count method of System.Linq.Enumerable
MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethod("Count", new Type[] { typeof(IEnumerable<>) });
PropertyInfo[] properties = typeof(MyCollections).GetProperties();
foreach (PropertyInfo property in properties)
{
Type propertyType = property.PropertyType;
if (propertyType.IsGenericType)
{
Type genericType = propertyType.GetGenericTypeDefinition();
if (genericType == typeof(IEnumerable<>))
{
// access the collection property
object collection = property.GetValue(someInstanceOfMyCollections, null);
// access the type of the generic collection
Type genericArgument = propertyType.GetGenericArguments()[0];
// make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection
MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument);
// invoke Count method (this fails)
object count = localCountMethodInfo.Invoke(collection, null);
System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count);
}
}
}
If you insist on doing it the hard way ;p
Changes:
how you obtain countMethodInfo for a generic method
the arguments to Invoke
Code (note obj is my instance of MyCollections):
MethodInfo countMethodInfo = typeof (System.Linq.Enumerable).GetMethods().Single(
method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1);
PropertyInfo[] properties = typeof(MyCollections).GetProperties();
foreach (PropertyInfo property in properties)
{
Type propertyType = property.PropertyType;
if (propertyType.IsGenericType)
{
Type genericType = propertyType.GetGenericTypeDefinition();
if (genericType == typeof(IEnumerable<>))
{
// access the collection property
object collection = property.GetValue(obj, null);
// access the type of the generic collection
Type genericArgument = propertyType.GetGenericArguments()[0];
// make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection
MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument);
// invoke Count method (this fails)
object count = localCountMethodInfo.Invoke(null, new object[] {collection});
System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count);
}
}
}
That is going to involve some MakeGenericMethod - and a lot of reflection generally. Personally, I would be tempted to just simplify by ditching the generics in this case:
public static int Count(IEnumerable data) {
ICollection list = data as ICollection;
if(list != null) return list.Count;
int count = 0;
IEnumerator iter = data.GetEnumerator();
using(iter as IDisposable) {
while(iter.MoveNext()) count++;
}
return count;
}
You can cast to the non-generic IEnumerable trivially, even if fetching via reflection.
By now, the question has been answered, but I'd like to present you a trimmed down — and I think, rather trivial version — of the "calling a generic extension method", which can be used to invoke Count reflectively:
// get Enumerable (which holds the extension methods)
Type enumerableT = typeof(Enumerable);
// get the Count-method (there are only two, you can check the parameter-count as in above
// to be certain. Here we know it's the first, so I use the first:
MemberInfo member = enumerableT.GetMember("Count")[0];
// create the generic method (instead of int, replace with typeof(yourtype) in your code)
MethodInfo method = ((MethodInfo) member).MakeGenericMethod(typeof(int));
// invoke now becomes trivial
int count = (int)method.Invoke(null, new object[] { yourcollection });
The above works, because you don't need to use the generic type of IEnumerable<> to be able to invoke Count, which is an extension of Enumerable and takes an argument of IEnumerable<T> as first param (it's an extension), but you don't need to specify that.
Note that, from the reading of your question, it seems to me that you should actually use generics for your types, which adds type safety back into your project and still allows you to use Count or whatever. After all, the one thing that's certain is that all are Enumerable, right? If so, who needs reflection?
var count = System.Linq.Enumerable.Count(theCollection);
Edit: you say it's generated though, so can you not just generate a properties with calls to Count()?
public class MyCollections
{
public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection;
public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection;
public int CountSomeTypeCollection
{
get { return this.SomeTypeCollection.Count(); }
}
...