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).
Related
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));
the question is : if i have MyConvertDataRowToEntity(DataRow row )
and I call in with T object from type Parent and inside I call the same function with desendant type Child how should I pass the DataRow parameter ?
The problem is created when Invoke of MakeGenericMethod called.
Did change the type to DataSet , string and String types .
No luck.
(I recognize the children object bu prefix in column names - PrefixDataColumn )
public static T MyConvertDataRowToEntity<T>(DataRow row ) where T : class, new()
{
Type objType = typeof(T);
Type parentObjType = typeof(T);
T obj = Activator.CreateInstance<T>(); //hence the new() contsraint
PropertyInfo propertyGenericType = null;
object childInstance = null;
PropertyInfo property;
string childColumnName = string.Empty ;
foreach (DataColumn column in row.Table.Columns)
{
column.ColumnName = column.ColumnName.Replace("_", "");
string PrefixDataColumn;
if (column.ColumnName.IndexOf(".") > (-1))
{
///gets the prefix that is the same as child entity name
PrefixDataColumn = column.ColumnName.Substring(0, column.ColumnName.IndexOf("."));
///the column name in the child
int length = column.ColumnName.Length - 1;
int start = column.ColumnName.IndexOf(".") + 1;
childColumnName = column.ColumnName.Substring(column.ColumnName.IndexOf(".") + 1);
propertyGenericType = objType.GetProperty(PrefixDataColumn,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
parentObjType = objType;
if (propertyGenericType != null)
{
Type childType = propertyGenericType.PropertyType;
objType = childType;
childInstance = Activator.CreateInstance(propertyGenericType.PropertyType);
// get the get method for the property
MethodInfo method = propertyGenericType.GetGetMethod(true);
// get the generic get-method generator
MethodInfo genericHelper = typeof(DataUtil).GetMethod("MyConvertDataRowToEntity", BindingFlags.Public | BindingFlags.Static);
List<Type> signature = new List<Type>();
// first type parameter is type of target object
signature.Add(childType);
//next parameters are real types of method arguments
foreach (ParameterInfo pi in genericHelper.GetParameters())
{
signature.Add(pi.ParameterType);
}
// last parameters are known types of method arguments
signature.AddRange(typeof(T).GetGenericArguments());
// reflection call to the generic get-method generator to generate the type arguments
//MethodInfo constructedHelper = genericHelper.MakeGenericMethod(signature.ToArray());
// reflection call to the generic get-method generator to generate the type arguments
MethodInfo constructedHelper = genericHelper.MakeGenericMethod(childType );
// now call it. The null argument is because it's a static method.
object ret = constructedHelper.Invoke(null, new object[] { method });
// object myObj = method.Invoke(null, row);
//// property.SetValue(obj, MyConvertDataRowToEntity<childInstance>(DataRow row),null);
// childInstance = DataUtil.GetMethod("MyConvertDataRowToEntity").MakeGenericMethod(childType); //MyConvertDataRowToEntity<object>(row);
//childType initializedChild = ;
//property.SetValue(obj, value, null);
//objType = parentObjType;
}
else
{
continue;
}
}
}
return obj;
}
Getting this error :
Object of type 'System.Reflection.RuntimeMethodInfo' cannot be converted to type 'System.Data.DataRow'.
Is there any solution for this ?
p.s.
Narrowed down the code as much as i could.
How can I invoke the Method recursivly with desedant types and pass datarow ?
The reason for the error is because you are calling MyConvertDataRowToEntity<ChildType> but then passing in the getaccessor methodinfo for the property as the parameter instead of a data row containing only those fields.
If you want to continue with the code processing logic you are currently using you would need to construct a new datarow containing the fields you wanted (with the prefix and ".") removed from the start of the column names.
Alternatively you could create a helper method the accepted a column name, the source object and it simply updated the value.
static void UpdateItemProperty<T>(T item, string columnName, object rowValue) {
var prefixColumn=columnName.IndexOf(".")==-1 ? columnName : columnName.Split(".")[0];
var pi = typeof(T).GetProperty(prefixColumn // Add your binding flags);
// if pi==null then there is an error...
if (column.ColumnName.IndexOf(".") == (-1)) { // No Nesting
pi.SetValue(item,rowValue);
return;
}
// Nesting
var child=pi.GetValue(item);
if (child==null) {
// Logic here to get childs type and create an instance then call pi.SetValue with child
}
var remainder=string.Join(',',columnName.Split(".").Skip(1).ToArray());
// make your generic method info for UpdateItemProperty with pi.PropertyType into mi
mi.Invoke(null,new object[] { child,remainder,value };
}
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?
I need to execute a method "FindAll" in my page. This method returns a list of the object.
This is my method that I execute "FindAll". FindAll requires an int and returns an List of these class.
public void ObjectSource(int inicio, object o)
{
Type tipo = o.GetType();
object MyObj = Activator.CreateInstance(tipo);
object[] args = new object[1];
args[0] = inicio;
List<object> list = new List<object>();
object method = tipo.InvokeMember("FindAll", BindingFlags.Default | BindingFlags.InvokeMethod, null, null, args);
}
When I execute ObjectSource, it returns ok, but I can't access the result. In VS2008, I can visualize the list by "ctrl + Alt + q" but by casting doesn't work.
I forgot to say: this method "FindAll" is static!
Few things going on here, first, your method doesn't return the result.
Second, when you do return the object, there's nothing stopping you casting to the appropriate type in the calling code.
Third, you could use Generics to make this method strongly typed like so:
public T ObjectSource<T>(int inicio, T o)
{
Type tipo = typeof(T);
object MyObj = Activator.CreateInstance(tipo);
object[] args = new object[1];
args[0] = inicio;
return tipo.InvokeMember("FindAll", BindingFlags.Default | BindingFlags.InvokeMethod, null, null, args) as T;
}
Try this (updated):
public IEnumerable ObjectSource(int inicio, object o) {
Type type = o.GetType();
object[] args = new object[] { inicio };
object result = type.InvokeMember("FindAll",
BindingFlags.Default | BindingFlags.InvokeMethod, null, o, args);
return (IEnumerable) result;
}
A better solution would be to put your FindAll method into an interface -- say, IFindable, and make all your classes implement that interface. Then you can just cast the object to IFindable and call FindAll directly -- no reflection required.
Daniel, i got a few object that i need to bind a grid view and this list more than 1.000 records, then a want to pagging by 50, and this Object Source must be generic because will call FindAll of the classes!