Hey,
On Thu, Jan 20, 2011 at 11:51 PM, José F. Romaniello
<[email protected]> wrote:
> ok, i have an assembly "A" just with these three things:
> //interface for template
> public interface IFoo<T>
> {
> }
> //template
> public class Foo<T> : IFoo<T>
> {}
> //target
> [ExtendWith(typeof(Foo<int>))]
> public class Target
> {
> }
> From another assembly "B", I am trying to modify this one, in order to
> make Target implement IFoo<int> (just that for now).
> So, i look all types that has the ExtendWith attribute, and then i read the
> constructor arguments and i get a GenericInstanceType pointing to Foo<int>
> and i have the Type Definition of Target.
> So, what is the best way to get IFoo<int> from the GenericInstanceType?
> If i do genericInstanceType.Resolve().Interfaces <- these generic instance
> types doesn't have the arguments...
So first of all, you need to take a few things into account. In one
assembly, you can get a generic instance of a type, without having all
the details of a type, that is, if the type is defined in another
assembly.
If you have GenericInstanceType, you usually use .ElementType to
figure out the type which is instantiated. If you use .Resolve(), it
will return the definition of the ElementType. Which means that it may
have to go look for it in another assembly.
And with Cecil, you can't directly use types defined in a module into
another module. You have to create references.
So in your scenario, if IFoo<T> is in IFoo.dll, if Foo<T> is is
Foo.dll, and Target in Target.dll, you have to take care of a few
things.
When you have the TypeDefinition `Target`, and that you get its custom
attribute, you'll get a TypeReference to Foo`1 (in that case, a
GenericInstanceType with the ElementType being Foo`1 and with a
generic argument being a reference to int32.
If you Resolve this reference, it will open Foo.dll and get you the
TypeDefinition of Foo`1.
If you browse its interfaces, you'll have a TypeReference to IFoo`1 .
If you Resolve this reference, it will open IFoo.dll and get you the
TypeDefinition of IFoo`1.
So now we're all down the chain, and you want to make Target implement
IFoo`1 with the generic arguments of the GenericInterfaceType in the
CustomAttribute.
You can't just add the interface, you have to create the proper references.
ModuleDefinition targetModule = ...;
TypeDefinition targetType = ...;
GenericInstanceType foo_of_int_ref = (GenericInstanceType)
targetType.CustomAttributes [0].ConstructorArguments [0];
TypeReference foo_of_t_ref = foo_of_int_ref.ElementType;
TypeDefinition foo_of_t = foo_of_t_ref.Resolve (); // opens Foo.dll,
foo_of_t.Module is Foo.dll
TypeReference ifoo_of_t_ref = foo_of_t.Interfaces [0];
TypeDefinition ifoo_of_t = ifoo_of_t_ref.Resolve (); // opens
IFoo.dll, ifoo_of_t.Module is IFoo.dll
So now, let's make targetType implement IFoo with the arguments passed to Foo.
TypeReference ifoo_ref_in_target = targetModule.Import (ifoo_of_t);
GenericInstanceType ifoo_instance_in_target = new GenericInstanceType
(ifoo_ref_in_target);
foreach (TypeReference argument in foo_of_int_ref)
ifoo_instance_in_target.GenericArguments.Add (argument);
targetType.Interfaces.Add (ifoo_instance_in_target);
And here we are. The important thing here is to get a proper reference
(import) for IFoo for the target module. Of course the code above is a
bit long and you cant short circuit a few things, but it helps
remembering all the different steps that need to happen.
Also note that in the loop:
foreach (TypeReference argument in foo_of_int_ref)
ifoo_instance_in_target.GenericArguments.Add (argument);
We can re-use the argument as they are, because they are already
scoped for the targetModule, after all, the original Foo<int>
instantiation is declared in targetModule, so we don't need to create
a proper reference for them.
Jb
--
--
mono-cecil