14

I'm having yet another nasty moment with Reflection.Emit and type management.

Say, I have a type named MyType which is defined in the dynamically generated assembly. Calling MyType.GetMethods() results in a NotSupportedException, which has reduced me to writing my own set of wrappers and lookup tables. However, the same is happening when I'm calling GetMethods() or any other introspecting methods on standard generic types which use my own types as generic arguments:

  • Tuple<int, string> => works fine
  • Tuple<int, MyType> => exception

I can get the method list from the generic type definition:

typeof(Tuple<int, MyType).GetGenericTypeDefinition().GetMethods()

However, the methods have generic placeholders instead of actual values (like T1, TResult etc.) and I don't feel like writing yet another kludge that traces the generic arguments back to their original values.

A sample of code:

var asmName = new AssemblyName("Test");
var access = AssemblyBuilderAccess.Run;
var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, access);
var module = asm.DefineDynamicModule("Test");

var aType = module.DefineType("A");
var tupleType = typeof(Tuple<,>);
var tuple = tupleType.MakeGenericType(new [] { typeof(int), aType });

tuple.GetProperty("Item1"); // <-- here's the error

So the questions are:

  1. How do I detect if a type is safe to call GetMethods() and similar methods on?
  2. How do I get the actual list of methods and their generic argument values, if the type is not safe?
Impworks
  • 2,647
  • 3
  • 25
  • 53
  • Please edit your question to include a short but *complete* program demonstrating the problem. Also, give more details about the `NotSupportedException` - is there nothing in the message? – Jon Skeet Mar 26 '13 at 20:44
  • @JonSkeet, the exception simply says "The method is not supported" or something like that. I'm running a Russian version of Windows, so I don't know the exact message, but it contains no additional information. The stacktrace only has my `GetMethods()` and `System.Reflection.Emit.TypeBuilderInstantiation.GetMethods(BindingFlags bindingAttr)` inside it. – Impworks Mar 26 '13 at 21:09
  • If you need those `MethodInfo`s (etc.) because you are still busy *building* the type, you do in fact have to keep track of the original `MethodBuilder`s (etc.) for use in method invocations and other emitted calls. If you need the `MethodInfo`s (etc.) after you've already called `CreateType`, then, as Eric answered, you can invoke those methods on the created type. – Kirk Woll Mar 26 '13 at 23:14
  • @KirkWoll, I'm busy building _another_ type. For example, I have types `A` and `B`. Type `B` has a method that uses `Tuple` and needs to get `tuple.Item1` out of it. I detect the type of `tuple` variable and call `GetProperties()` on it. There are no `MethodBuilder`s to track since I have not defined this type, but merely instantiated it by applying generic arguments. – Impworks Mar 27 '13 at 07:07
  • @JonSkeet, I have added a small snippet of code. – Impworks Mar 27 '13 at 07:12

2 Answers2

7

I got an answer in a follow-up question. The TypeBuilder class has a bunch of static overloads which do exactly the thing:

var genericTuple = typeof(Tuple<,>);
var myTuple = genericTuple.MakeGenericType(typeof(int), myType);
var ctor = TypeBuilder.GetConstructor(myTuple, genericTuple.GetConstructors().First());

Strangely, there's no overload of GetProperty. However, property getters and setters can still be resolved using GetMethod:

var genericGetter = typeof(Tuple<,>).GetProperty("Item1").GetMethod;
var actual = TypeBuilder.GetMethod(myTuple, genericGetter);
Community
  • 1
  • 1
Impworks
  • 2,647
  • 3
  • 25
  • 53
4

I'll refer you to the documentation:

Defining a Type with Reflection Emit

which says

Although TypeBuilder is derived from Type, some of the abstract methods defined in the Type class are not fully implemented in TypeBuilder. These TypeBuilder methods throw the NotSupportedException. The desired functionality can be obtained by retrieving the created type using Type.GetType or Assembly.GetType and reflecting on the retrieved type.

So:

How do I detect if a type is safe to call GetMethods() and similar methods on?

If the type object is a TypeBuilder, or if the type refers to any type that is a TypeBuilder, then it is not safe to do so.

How do I get the actual list of methods and their generic argument values, if the type is not safe?

I'll refer you to the documentation again. Actually emit the type into an assembly and then get it out of the assembly.

svick
  • 236,525
  • 50
  • 385
  • 514
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Checking if a type is a `TypeBuilder` is easy, but how do I check if a type *refers* to a `TypeBuilder`? – Impworks Mar 27 '13 at 07:15
  • @Impworks: Why do you need to check it? Just do all your type building, emit the assembly, and *then* start using the types. It's not really clear what your bigger aim is, but Eric's advice looks like the way forward to me. – Jon Skeet Mar 27 '13 at 07:17
  • As for the idea to emit the type into assembly, types may be cross-dependent. Like, `A` uses a `List` and `B` uses a `List`. I can't emit either type completely and finalize it with `CreateType`. – Impworks Mar 27 '13 at 07:17
  • @Impworks: Why do you need to use `GetMethods()` while defining those types though? Define both of them, then emit the assembly, then do whatever you need with them. It sounds like there's a lot of context here that you haven't included in your question. – Jon Skeet Mar 27 '13 at 07:18
  • @JonSkeet, I'm creating a compiler that has two phases: during the first I create a `TypeBuilder` for each type defined in the source, during the second I generate the code of the methods. Generating the code requires a lot of lookups for types that might be returned from other lookups. – Impworks Mar 27 '13 at 07:20
  • 4
    @Impworks: Ah, now we come to it. **Reflection.Emit is too weak to use to build a real compiler**. It's great for little toy compilation tasks like emitting dynamic call sites and expression trees in LINQ queries, but for the sorts of problems you'll face in a compiler you will quickly exceed its capabilities. Use CCI, not Reflection.Emit. – Eric Lippert Mar 27 '13 at 13:55