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);
}
Related
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);
Sorry for my English Miserables.
I have 2 values from an XML file and I need it to a generic method further lead to the return-value T. In one XML value is of this type, but how can I use this as a Type?
example:
var dataType = xml.Element("type").Value;
var modelList = await Mapper<dataType>(serviceXml, myNamespace, objType, serviceJson.ToString());
1) Load type with Type.GetType
2) Use reflection to create generic Mapper, i.e. typeof(Mapper<>).CreateGenericType(dataType)
3) Use Activator.CreateInstance to create instance of generic Mapper
You can't use a type variable as a parameter in a generic method. The type must be known at compile time. You can do it through reflection by calling a method created using the MakeGenericMethod call. I think it would look something like this:
var method = Mapper.GetType().GetMethod("DoesEntityExist")
.MakeGenericMethod(new Type[] { dataType });
method.Invoke(this, new object[] { serviceXml, myNamespace, objType, serviceJson.ToString() });
What you could do is create an interface or a base class and create an extension method to handle the conversion.
You can use the static GetType method that takes a string and return a Type if it can be found
Type.GetType("System.Collections.Generic.Dictionary`2[System.String,[MyType,MyAssembly]]")
If the type represented by the string cannot be found, it will return null
After getting the type, you can use the Mapper through reflection: if Mapper is a method then use the following
MethodInfo method = this.GetType().GetMethod("Mapper"); // this.GetType works if you aren't in a static method
MethodInfo generic = method.MakeGenericMethod(Type.GetType("theXmlValue"));
generic.Invoke(this, new object[] {/* your parameters to the Mapper method */});
We have a compiler that uses reflection emit to generate assemblies. We have stumbled with trying to obtain the MethodInfo for the Add method in the BindingList<T> class, when T is a TypeBuilder object. We are using TypeBuilder.GetMethod( typeof(BindingList<myTypeBuilder>), typeof(BindingList<T>).GetMethod( "Add" )) but it throws an ArgumentException: "The specified method cannot be dynamic or global and must be declared on a generic type definition." Are we doing anything wrong? This works for List. Another observation, typeof( List<> ).GetMethod( "Add" ).DeclaringType.IsGenericTypeDefinition is true, while
typeof( BindingList<> ).GetMethod( "Add" ).DeclaringType.IsGenericTypeDefinition is false, which doesn't make sense to me.
Here's C# code that recreates the issue
var assemblyBuilder = System.Threading.Thread.GetDomain().DefineDynamicAssembly( new AssemblyName("MyAssembly"), AssemblyBuilderAccess.Save, "C:\\output\\" );
var moduleBuilder = assemblyBuilder.DefineDynamicModule( "MyModule", "MyModule.dll", false );
// create MyClass in the module
TypeBuilder myClass = moduleBuilder.DefineType( "MyClass" );
Type bindingListOfT = typeof( BindingList<> );
Type bindingListOfMyClass = bindingListOfT.MakeGenericType( myClass );
// create the DoStuff method in MyClass
MethodBuilder doStuffMethod = myClass.DefineMethod( "DoStuff", MethodAttributes.Private );
ILGenerator generator = doStuffMethod.GetILGenerator();
// var myList = new BindingList<MyClass>()
LocalBuilder myListDeclaration = generator.DeclareLocal( bindingListOfMyClass );
ConstructorInfo listOfMyClassConstructor = TypeBuilder.GetConstructor( bindingListOfMyClass,
bindingListOfT.GetConstructor( Type.EmptyTypes ) );
generator.Emit( OpCodes.Newobj, listOfMyClassConstructor );
generator.Emit( OpCodes.Stloc, myListDeclaration );
// myList.Add( new MyClass() )
ConstructorInfo myClassConstructor = myClass.DefineConstructor( MethodAttributes.Public,
CallingConventions.Standard, Type.EmptyTypes );
generator.Emit( OpCodes.Ldloc, myListDeclaration );
generator.Emit( OpCodes.Newobj, myClassConstructor );
// the next line throws exception. If using List<> instead on BindingList<> all is well
MethodInfo add1 = TypeBuilder.GetMethod( bindingListOfMyClass, bindingListOfT.GetMethod( "Add" ) );
generator.Emit( OpCodes.Callvirt, add1 );
// finish
generator.Emit( OpCodes.Ret );
myClass.CreateType();
assemblyBuilder.Save( "MyModule.dll", PortableExecutableKinds.ILOnly, ImageFileMachine.I386 );
We have found a workaround which involves getting the declaring type's generic type definition, finding the MethodInfo in this type, making a generic type out of the generic type definition, and then calling TypeBuilder.GetMethod. It's a horrible piece of code, as first we need to find the correct MethodInfo not only based on name but also on arguments, and then climb back the inheritance chain of the original type so we can correctly match the type arguments in the base class to call MakeGenericType, all the way back to the method's declaring type. There has to be an easier way.
Add is actually declared on System.Collections.ObjectModel.Collection<>, so try using that type instead of BindingList<> when creating your two Type instances.
EDIT
As to why BindingList<T>'s base class is not a generic type definition, it's a bit subtle. While the base class is Collection<T>, the T is actually different from the T used in Collection<T>'s definition (it's the T declared as part of BindingList<T>'s definition). Perhaps an easier-to-grasp example would be something along these lines:
class SameTypeDict<T> : Dictionary<T,T> {}
Here, SameTypeDict<T>'s base class is Dictionary<T,T>, which is different from the generic type definition Dictionary<TKey,TValue>.
I think something along these lines should do what you want:
/// <summary>
/// Given a concrete generic type instantiation T<t_1, t_2, ...>, where any number of the t_i may be type builders,
/// and given a method M on the generic type definition T<>, return the corresponding method on the concrete type
/// </summary>
static MethodInfo GetConcreteMethodInfo(Type concreteClass, MethodInfo genericTypeMethod)
{
var substitution = concreteClass.GetGenericTypeDefinition().GetGenericArguments()
.Zip(concreteClass.GetGenericArguments(), (t1, t2) => Tuple.Create(t1, t2))
.ToDictionary(t => t.Item1, t => t.Item2);
var declaredMethod = genericTypeMethod.DeclaringType.Module.ResolveMethod(genericTypeMethod.MetadataToken);
var declaringTypeGeneric = declaredMethod.DeclaringType; // typeof(Collection<>)
var declaringTypeRelative = genericTypeMethod.DeclaringType; // typeof(Collection<BindingList<T>.T>)
// now get the concrete type by applying the substitution
var declaringTypeConcrete = // typeof(Collection<myClass>)
declaringTypeGeneric.MakeGenericType(Array.ConvertAll(declaringTypeRelative.GetGenericArguments(),
t => substitution.ContainsKey(t) ? substitution[t] : t));
return TypeBuilder.GetMethod(declaringTypeConcrete, (MethodInfo)declaredMethod);
}
Note that you may need to make some modifications if the method itself can also be generic.
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));
This is probably best shown with an example. I have an enum with attributes:
public enum MyEnum {
[CustomInfo("This is a custom attrib")]
None = 0,
[CustomInfo("This is another attrib")]
ValueA,
[CustomInfo("This has an extra flag", AllowSomething = true)]
ValueB,
}
I want to get to those attributes from an instance:
public CustomInfoAttribute GetInfo( MyEnum enumInput ) {
Type typeOfEnum = enumInput.GetType(); //this will be typeof( MyEnum )
//here is the problem, GetField takes a string
// the .ToString() on enums is very slow
FieldInfo fi = typeOfEnum.GetField( enumInput.ToString() );
//get the attribute from the field
return fi.GetCustomAttributes( typeof( CustomInfoAttribute ), false ).
FirstOrDefault() //Linq method to get first or null
as CustomInfoAttribute; //use as operator to convert
}
As this is using reflection I expect some slowness, but it seems messy to convert the enum value to a string (which reflects the name) when I already have an instance of it.
Does anyone have a better way?
This is probably the easiest way.
A quicker way would be to Statically Emit the IL code using Dynamic Method and ILGenerator. Although I've only used this to GetPropertyInfo, but can't see why you couldn't emit CustomAttributeInfo as well.
For example code to emit a getter from a property
public delegate object FastPropertyGetHandler(object target);
private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type)
{
if (type.IsValueType)
{
ilGenerator.Emit(OpCodes.Box, type);
}
}
public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo)
{
// generates a dynamic method to generate a FastPropertyGetHandler delegate
DynamicMethod dynamicMethod =
new DynamicMethod(
string.Empty,
typeof (object),
new Type[] { typeof (object) },
propInfo.DeclaringType.Module);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
// loads the object into the stack
ilGenerator.Emit(OpCodes.Ldarg_0);
// calls the getter
ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null);
// creates code for handling the return value
EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType);
// returns the value to the caller
ilGenerator.Emit(OpCodes.Ret);
// converts the DynamicMethod to a FastPropertyGetHandler delegate
// to get the property
FastPropertyGetHandler getter =
(FastPropertyGetHandler)
dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler));
return getter;
}
I generally find reflection to be quite speedy as long as you don't dynamically invoke methods.
Since you are just reading the Attributes of an enum, your approach should work just fine without any real performance hit.
And remember that you generally should try to keep things simple to understand. Over engineering this just to gain a few ms might not be worth it.