How to invoke Generic Method with Generic Parameter in C#? - c#

I would like to know how to use reflection in C# to call the following method :
public static List<T> GetAllWithChildren<T>
(this SQLiteConnection conn, Expression<Func<T, bool>> filter = null, bool recursive = false)
where T
#if USING_MVVMCROSS: new() #else : class #endif
{
}
My current code is:
MethodInfo methodInfo = typeof(ReadOperations).GetMethod("GetWithChildren", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
Type predicateType = predicate.GetType();
MethodInfo genericMethod = methodInfo.MakeGenericMethod(predicateType);
Type[] genericArgumentsType = genericMethod.GetGenericArguments();
Debug.WriteLine("Arguments Number:" + genericArgumentsType.Count());
int count = 0;
foreach (Type ga in genericArgumentsType)
{
Console.WriteLine(count++ + " " + ga.GetType());
}
Object[] genericArguments = { conn, predicate, true };
genericMethod.Invoke(conn, genericArguments);
The number of arguments returned is 1 ... that it's wrong but I don't know why the system return to me this number.
The invoke method fail with a wrong number of arguments.
Any help will be welcome!

You're using the Predicate's Generic argument to Make the method generic. which means:
Generic argument of Expression<Func<T, bool>> would be Func<T, bool> which is not actual type you're looking for to mark the method with. Update the following lines:
Type predicateType = predicate.GetType();
MethodInfo genericMethod = methodInfo.MakeGenericMethod(predicateType);
To
Type parameterType = predicate.Parameters[0].Type;
MethodInfo genericMethod = methodInfo.MakeGenericMethod(parameterType);
This will give you the type of T from Func<T,bool>. Now It should work as expected.
Above change are based on assumption that you're predicate is of type Expression<Func<T, bool>>. In case the predicate is Func<T, bool> then parameterType can be fetched like below:
Type parameterType = predicate1.GetType().GetGenericArguments()[0];

You're calling it with GetWithChildren, instead of GetAllWithChildren.

your generic method is extension method....Change below code and try again
MethodInfo methodInfo = typeof(**SQLiteConnection**).GetMethod("GetAllWithChildren", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);

Related

How to call IQueryable.OrderBy() method with a runtime type for generic parameter?

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

How to get the MethodInfo for Enumerable.SequenceEqual

I'm trying to get the MethodInfo for Enumerable.SequenceEqual, using Type.GetMethod(...). So far I have tried the following:
var mi = typeof(Enumerable).GetMethod(nameof(Enumerable.SequenceEqual),
BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any,
new Type[] { typeof(IEnumerable<>), typeof(IEnumerable<>) }, null);
and
var enumTyped = typeof(IEnumerable<>).MakeGenericType(ValueType);
var mi = typeof(Enumerable).GetMethod(nameof(Enumerable.SequenceEqual),
BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any,
new Type[] { enumTyped, enumTyped }, null);
However, both solutions return null instead of the method I want. I know the method is retrievable by calling GetMethods() and filtering, but I'd very much like to know how to retrieve it using GetMethod(...).
Unfortunately, in order to get the generic generic methods using Type.GetMethod(string name, Type[] types) you have to provide the method the right generic types in the Type[], which means that when you try to do this:
Type requiredType = typeof(IEnumerable<>);
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { requiredType, requiredType });
You actually needed to do something like that:
Type requiredType = typeof(IEnumerable<TSource>);
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { requiredType, requiredType });
Since if you look at the signature of SequenceEqual, the generic type is IEnumerable<TSource> not IEnumerable<>.
public static IEnumerable<TSource> SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);
BUT: you don't have access to the type TSource in order use it.
So the only way to get IEnumerable<TSource> is using reflection like the following:
MethodInfo info = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(x => x.Name.Contains("SequenceEqual"))
.Single(x => x.GetParameters().Length == 2);
Type genericType = typeof(IEnumerable<>).MakeGenericType(infos.GetGenericArguments());
and than getting the method using
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { genericType, genericType });
But this requires us to get the SequenceEqual method anyway, so the sad fact is getting the method a generic method when there are few overloads using GetMethod instead of GetMethods is practically impossible* (You CAN implement a Binder and use it in the GetMethod method but it will require very long coding which will possibly be buggy and unmaintainable and should be avoided).
Want to add to previous answers a bit. First, it's indeed not possible to use single GetMethod to do what you want. But, if you don't want to call GetMethods and get all 180+ methods of Enumerable, you can do this:
var mi = typeof(Enumerable).GetMember(nameof(Enumerable.SequenceEqual), MemberTypes.Method,
BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod).OfType<MethodInfo>().ToArray();
GetMember call will return you just 2 overloads of SequenceEqual method of which you can choose one and do MakeGenericMethod as shown in other answers.
Also, depending on your goal, you may consider to use expressions:
var source = Expression.Parameter(
typeof(IEnumerable<string>), "source");
var target = Expression.Parameter(
typeof(IEnumerable<string>), "target");
var callExp = Expression.Call(typeof(Enumerable), "SequenceEqual", new Type[] { typeof(string)},
source, target);
var lambda = Expression.Lambda<Func<IEnumerable<string>, IEnumerable<string>, bool>>(callExp, source, target).Compile();
var result = lambda(new[] { "1", "2", "3" }, new[] { "1", "2", "3" });
Debug.Assert(result);
Another one a bit different answer based on two previous answers but with the MakeGenericMethod call and VisualBasic version.
Dim lSequanceEqual As MethodInfo = GetType(System.Linq.Enumerable).GetMethods()
.First(Function(mi) mi.Name.Contains(NameOf(System.Linq.Enumerable.SequenceEqual)) AndAlso mi.GetParameters.Length = 2)
Dim lMethod As MethodInfo = lSequanceEqual.MakeGenericMethod(New Type() { GetType(String)})
if CBool(lMethod.Invoke(Nothing, New Object() { firstList, secondList})) Then 'Do something

How do you call a generic method with out parameters by reflection?

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

How to create a lambda expression from a type object with LINQ?

I'm trying to speed up reflection -> SetValue with a LINQ expression.
My problem is this method:
public void SetValue<T>(T obj)
{
FieldInfo field = typeof(T).GetField("Title", BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.IgnoreCase);
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
ParameterExpression valueExp = Expression.Parameter(field.FieldType, "value");
// Expression.Property can be used here as well
MemberExpression fieldExp = Expression.Field(targetExp, field);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
var setter = Expression.Lambda<Action<T, string>>(assignExp, targetExp, valueExp).Compile();
setter(obj, "Hello World");
//Console.WriteLine(obj.title);
}
which I call like this:
var ii = new Controllers.SearchController.InstantItem();
SetValue<Controllers.SearchController.InstantItem>(ii);
The problem is this line:
var setter = Expression.Lambda<Action<T, string>>(assignExp, targetExp, valueExp).Compile();
Because Action uses generics, I cannot replace string with field.FieldType...
Is there any possibility I can do this without having to make a switch(field.FieldType) statement, and put a generic method for each possible type, which would suck big time?
Maybe something like this
action = FormAction(fieldInfo);
action(obj,valueToSet);
Of course caching the Actions in a dictionary will be needed.
static Action<object, object> FormAction(FieldInfo fieldInfo)
{
ParameterExpression obj = Expression.Parameter(typeof(object), fieldInfo.Name);
ParameterExpression value = Expression.Parameter(typeof(object));
MemberExpression fieldExp = Expression.Field(Expression.Convert(obj, fieldInfo.DeclaringType), fieldInfo.Name);
BinaryExpression assignExp = Expression.Assign(fieldExp, Expression.Convert(value, fieldInfo.FieldType));
return Expression.Lambda<Action<object, object>>(assignExp, obj, value).Compile();
}
Make the parameter ob type object and make the lambda cast to the correct field-type internally.
Or, construct the delegate type (Action) dynamically at runtime.
Or, you could define SetValue as follows:
SetValue<TObject, TProperty>
That would make this method generic over the property type.
Edit: It sounds like option 1 is best for you. You need to change the parameter type to typeof(object) and add a cast:
valueExp = Expression.Convert(valueExp, field.FieldType)
And you need to use Action<T, object> as the delegate type.

How to set parameter of method using reflection

The following code is a helper I've created for a system which allows administrators to create their own queries on a database. It returns a lambda expression based on the method and value provided.
I am attempting to find a way to pass parameters to the method call - in this example I am using the StartsWith parameter of String, and attempting to set StringComparison.OrdinalIgnoreCase as a parameter.
There will be others parameters required too, depending on the type of the property specified. I'm hoping that understanding the method of supplying the string comparison property will enable me to add the rest later.
The underlying code works correctly, I just need it to be case-insensitive.
I have used this question as a guide, but the solution does not seem applicable here.
Here is the code:
public static class LambdaExpressionHelper<T> {
public static Expression<Func<T, bool>> Build(string propertyName, string method, string propertyValue) {
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
ConstantExpression c = Expression.Constant(propertyValue, m.Type);
MethodInfo mi = m.Type.GetMethod(method, new Type[] { m.Type }, );
// The below caused errors
//object classInstance = Activator.CreateInstance(typeof(T), null);
//object[] paramArray = new object[] { StringComparison.OrdinalIgnoreCase };
//mi.Invoke(classInstance, paramArray);
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
}
// External code:
var lambda = LambdaExpressionHelper<MailingListMember>.Build("EmailAddress", "StartsWith", "RoRy#");
Thanks for any help.
In the general case this should work:
public static Expression<Func<T, bool>>
Build(string propertyName, string method, params object[] args)
{
var propertyInfo = typeof(T).GetProperty(propertyName);
var e = Expression.Parameter(typeof(T), "e");
var m = Expression.MakeMemberAccess(e, propertyInfo);
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType())).ToArray();
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
The idea is that you accept any number of constant arguments, and use their types to select the appropriate overload when calling GetMethod. After that, you create the appropriate number of constant expressions from those arguments to pass to Expression.Call.
So with the above code, you could do:
var l1 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#");
var l2 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#", StringComparison.OrdinalIgnoreCase);
If you also need to get the value StringComparison.OrdinalIgnoreCase from the string "StringComparison.OrdinalIgnoreCase", I would factor this out into a separate method so that the interface of Build can remain generic. Exactly how to do it is covered in Getting Enum value via reflection (and I guess in lots of similar questions as well).
Note: I don't have convenient access to a compiler right now, so please excuse any mistakes.
I'm not sure i understand your question exactly, but hopefully this will help you on the way.
If you are using .NET 4.0 you can use the new keyword "dynamic" for much easier reflection code:
dynamic myDynamicObj = "Hello World!"; // or Activator.CreateInstance...
var doesIndeed = myDynamicObj.StartsWith("Hello", StringComparison.OrdinalIgnoreCase);
This does evaluate at run time so make sure spelling/case etc is correct.
Edit:
Assuming you always wanted to call methods with one String-arg returning bool, a possible solution would be to create a delegate type -
delegate bool CompareString(String str);
and than have the second argument of Build as that type:
Build(String .., CompareString cs, String ...)
But this does not work if you need to add extra arguments, as in the second arg of type StringComparison. A if/switch could be the answer there though, i.e if(CompareString is StartsWith)...
Sorry, not at a windows-computer so i can't test further.

Categories