Skip to content
Tacodude7729 edited this page Aug 8, 2022 · 5 revisions

The .net type system that you can manipulate through traditional Reflection erases the difference between a reference and a definition. It only provides you with resolved types, fields and methods. At the metadata level, and thus, at the Cecil level, we make a distinction between a reference and a definition.

They are both scoped per ModuleDefinition. So, in a ModuleDefinition, a definition is a metadata element defined in the module itself, and a reference is a metadata element defined in another module. For instance, if you have an assembly Foo.dll, which contains the type Foo.Bar. This type Foo.Bar exposes a method, Baz that returns a System.String.

If you load Foo.dll using Cecil, Foo.Bar will be a TypeDefinition, and the return type of the method Baz will be a TypeReference to System.String. System.String is defined in the assembly mscorlib.dll, which means that if you open mscorlib.dll with Cecil, there will be a System.String TypeDefinition in it.

The process of getting a definition from a reference is called resolving. All resolvable references in Cecil have the Resolve method. For instance, let’s say you have loaded Foo.dll in Cecil, and that you want to Resolve the return type of the Baz method:

ModuleDefinition module = ModuleDefinition.ReadModule ("Foo.dll");

TypeDefinition foo_bar = module.Types.First (t => t.FullName == "Foo.Bar");
MethodDefinition baz = foo_bar.Methods.First (m => m.Name == "Baz");

TypeReference string_reference = baz.ReturnType;

// resolve into a definition:

TypeDefinition string_definition = string_reference.Resolve ();
ModuleDefinition corlib = string_definition.Module;

In this example, string_definition will be a TypeDefinition, and its module will be the ModuleDefinition for mscorlib.dll. To find the assembly that defines the reference, Cecil uses the IAssemblyResolver of the ModuleDefinition for Foo.dll.

We strongly suggest that you can specify your own IAssemblyResolver when reading a ModuleDefinition. This way you control the life-span of the module you load, and its associated references you resolve.

If you don’t specify a custom resolver, each module will have its own resolver, causing a greater memory usage.