Things in Metadata that are missing in Reflection

System.Reflection is a high-level way of describing Types in .NET targetted at managed code consumers. The API is easy to use, but does not expose all the information that’s actually present and affecting decisions.

For example, Reflection does not expose TypeRef, MemberRef, AssemblyRef, or other Ref tokens.  These tokens are references to things in other assemblies. Reflection just resolves them for you (potentially invoking an event to get help from your app) and hands back the resolved object.

Similarly, reflection is also missing TypeSpecs. TypeSpecs are just binary signature blobs that describe compound types (arrays, generics, etc). Reflection will parse  the blob and resolve it to a real System.Type.

This entry is not a complete list of all things missing in reflection; nor am I going to get into the other problems in reflection here. For now, I’ll just look at TypeRefs…

Imagine you have a class that inherits from a type in another assembly. At the metadata level, the base type is described with a TypeRef token.

1. Practically, that means you could use reflection to inspect what a base type actually bound to,  but not what it was supposed to bind to (as described in the original assemblyRef).

2. Another issue is that when you have a high-level API (Reflection) that loses information over a low level API (IMetadataImport), you risk that the high-level API won’t be able to talk to the low level API because it may not be able to provide it with the information the low level API requests.

3. In related trivia, ILDasm can print TypeRef, TypeSpec tokens:

//000010:         Console.WriteLine(“Hi!” + arg);
  IL_0001:  ldstr      “Hi!” /* 70000001 */
  IL_0006:  ldarg.0
  IL_0007:  call       string [mscorlib/*23000001*/]System.String/*01000012*/::Concat(string,
                                                                                      string) /* 0A000010 */
  IL_000c:  call       void [mscorlib/*23000001*/]System.Console/*01000013*/::WriteLine(string) /* 0A000011 */
  IL_0011:  nop

So the inability to get the raw unresolved tokens from Reflection would prevent you from writing ILDasm on reflection that could print the above snippet.

Comments (1)

  1. After 3 years coping with Reflection in the code of the static analyzer NDepend I came to the conclusion that System.Reflection must not be used to do any kind of code analysis.

    I think it was originaly made for implementing things like Plug-In pattern and it should not be used for something else.

    The main coarse problem is that it cannot consider code as raw data. For reflection code will be still considered as something more. And as a result:

    -You will get CAS exception, unexpected exception on static cctor… (no matter you are using the ReadOnly API)

    -It doesn’t cop well with complex C++/CLI assemblies (that often don’t respect the CLI).

    -It doesn’t cop well with 64bits assemblies on 32 bits machine and vice versa.

    -It has poor performance, certainly because of underlying CAS security check.

    -You cannot unload an assembly once it is loaded into an appdomain.

    But also:

    -It doesn’t parse IL.

    -And as you mention it doesn’t know about typeSpec

    I don’t say that Reflection is crap, it is a great tool for Plug-In scenario.

    But if you plan to use it to do any kind of analysis (as building your own ILDasm) use Mono.Cecil instead. This is what NDepend uses now and it is truly perfect for analysis (and open-source btw).