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 onSystem.Collections.ObjectModel.Collection<>
, so try using that type instead ofBindingList<>
when creating your twoType
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 isCollection<T>
, theT
is actually different from theT
used inCollection<T>
's definition (it's theT
declared as part ofBindingList<T>
's definition). Perhaps an easier-to-grasp example would be something along these lines:Here,
SameTypeDict<T>
's base class isDictionary<T,T>
, which is different from the generic type definitionDictionary<TKey,TValue>
.I think something along these lines should do what you want:
Note that you may need to make some modifications if the method itself can also be generic.