Error binding to target method - c#

MethodInfo method = typeof(T).GetMethod("Parse", new[] { typeof(string) });
parse = Delegate.CreateDelegate(typeof(Func<T,string>), method);
T is a float in this case. However I am getting a Error binding to target method. Parse I believe is a static method. I have looked at other examples, but I can not figure out why it is not binding.

you have to swap T and string because the method returns a T not a string.
I replaced T with float and following code works for me:
MethodInfo method = typeof(float).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string) }, null);
var parse = Delegate.CreateDelegate(typeof(Func<string, float>), method);
source: VS intellisense and MSDN Func(Of T, TResult) Delegate

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

In C# how does one call a static method using emit

I am trying to using Emit to generate mapping code (mapping properties from one object to another). I have it working if the two types match (source and target), but I can't get it to work in an instance where the types don't match and I need to call a static method in the mapping. Below is code that I thought would work but I get a method does not exist error even though it does. I am guessing my emit call is incorrect. Any suggestions?
foreach (var map in maps)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Callvirt, map.SourceProperty.GetGetMethod(), null);
if (map.SourceProperty.PropertyType == map.TargetProperty.PropertyType)
il.EmitCall(OpCodes.Callvirt, map.TargetProperty.GetSetMethod(), null);
else if (map.TargetProperty.PropertyType.Name == "ObjectId" && map.SourceProperty.PropertyType.Name.ToLower() == "string") {
var method = typeof(ObjectId).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new Type[] { typeof(string) }, null);
il.EmitCall(OpCodes.Callvirt, method , null);
}
}
il.Emit(OpCodes.Ret);
You should be able to call it by using EmitCall with OpCodes.Call instead of CallVirt.
This is the line that's throwing the error?
var method = typeof(ObjectId).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new Type[] { typeof(string) }, null);
Perhaps you could try
Type ObjectIDType = typeof(ObjectId);
MethodInfo method = ObjectIDType.GetMethod("Parse", new Type[] { typeof(string) });
Perhaps parse takes an object as its parameter instead of a string?
What is the error message?

Reflection on a static overloaded method using an out parameter

I'm having some issues with invoking an overloaded static method with an out parameter via reflection and would appreciate some pointers.
I'm looking to dynamically create a type like System.Int32 or System.Decimal, and then invoke the static TryParse(string, out x) method on it.
The below code has two issues:
t.GetMethod("TryParse", new Type[] { typeof(string), t } ) fails to return the MethodInfo I expect
mi.Invoke(null, new object[] { value.ToString(), concreteInstance }) appears to succeed but doesn't set the out param concreteInstance to the parsed value
Interwoven into this function you can see some temporary code demonstrating what should happen if the type parameter was set to System.Decimal.
public static object Cast(object value, string type)
{
Type t = Type.GetType(type);
if (t != null)
{
object concreteInstance = Activator.CreateInstance(t);
decimal tempInstance = 0;
List<MethodInfo> l = new List<MethodInfo>(t.GetMethods(BindingFlags.Static | BindingFlags.Public));
MethodInfo mi;
mi = t.GetMethod("TryParse", new Type[] { typeof(string), t } ); //this FAILS to get the method, returns null
mi = l.FirstOrDefault(x => x.Name == "TryParse" && x.GetParameters().Length == 2); //ugly hack required because the previous line failed
if (mi != null)
{
try
{
bool retVal = decimal.TryParse(value.ToString(), out tempInstance);
Console.WriteLine(retVal.ToString()); //retVal is true, tempInstance is correctly set
object z = mi.Invoke(null, new object[] { value.ToString(), concreteInstance });
Console.WriteLine(z.ToString()); //z is true, but concreteInstance is NOT set
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
return concreteInstance;
}
return value;
}
What do I need to do to ensure that my t.GetMethod() call returns the correct MethodInfo? What do I need to do to have concreteInstance correctly set in my mi.Invoke() call?
I know there are a bunch of questions on this topic, but most of them involve static generic methods or static methods that are not overloaded. This question is similar but not a duplicate.
You need to use the right BindingFlags and use Type.MakeByRefType for out and ref parameters. One second, and I'll have a code sample for you.
For example,
MethodInfo methodInfo = typeof(int).GetMethod(
"TryParse",
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
new[] { typeof(string), typeof(int).MakeByRefType() },
null
);
I should point out that invoking this is a little tricky too. Here's how you do it.
string s = "123";
var inputParameters = new object[] { "123", null };
methodInfo.Invoke(null, inputParameters);
Console.WriteLine((int)inputParameters[1]);
The first null is because we are invoking a static method (there is no object "receiving" this invocation). The null in inputParameters will be "filled" for us by TryParse with the result of the parse (it's the out parameter).

Categories