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?
Related
Can someone explain me, why here I get the correct MethodInfo:
MethodInfo mi = typeof(ContextTimerMode).GetMethod(_vo.Phase, BindingFlags.Instance | BindingFlags.NonPublic);
if (mi != null)
{
mi.Invoke(this, new object[] { btns, vo });
}
While trying to get it directly from instance returns always null?:
MethodInfo mi = (this as ContextTimerMode).GetType().GetMethod(_vo.Phase, BindingFlags.Instance | BindingFlags.NonPublic);
// mi always null
if (mi != null)
{
mi.Invoke(this, new object[] { btns, vo });
}
The code above is from ContextTimerMode.
this is a class that has ContextTimerMode as base class;
Actually I couldn't find a reason why it returns a null for sometime as I was using second method and I was debugging and making sure _vo.Phase string has correct name of method so I tried first method and it worked out.
Also when debugging this was showing that this is instance not of ContextTimerMode but a type that has a base of ContextTimerMode - so that is why I tried with (this as ContextTimerMode).GetType()...
That is because even if you do this:
(this as ContextTimerMode).GetType()
resulting type will not be ContextTimerMode, but will be the type inherited from ContextTimerMode (so, actual type of this, same as if you did this.GetType()). GetType() always returns actual type, even if you use it on variable declared as some base type. You are trying to get private, instance method of that type. Your inherited type really does not contain this method, so GetMethod correctly returns null.
If you want to work around this, you can manually walk hierarchy, like this:
static MethodInfo GetMethod(Type type, string methodName, BindingFlags flags) {
var mi = type.GetMethod(methodName, flags);
if (mi != null)
return mi;
if (type.BaseType != null)
return GetMethod(type.BaseType, methodName, flags);
return null;
}
The this keyword is redundant. You can just use GetType() internally and it will refer to your class.
_vo.Phase
must be the correct case and you don't need BindingFlags.Instance. I would remove BindingFlags.NonPublic as well and change the method access modifier to public to see if GetMethod() can actually find it.
You can always use GetMethods() to enumerate the available methods in the class. Maybe even use a linq query to get the MethodInfo object you want.
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
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));
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
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).