How to get generic arguments of a base type or an interface.

1,247 views
Skip to first unread message

Steves

unread,
Apr 27, 2011, 12:10:40 PM4/27/11
to mono-cecil
Hi there,

I will try to explain it on an example: let's say we have a class
MyList (which inherits from List<int>), I would like to have an
extension method that I would give MyList and IEnumerable<> (as
TypeDefinitions) and it would return a collection of TypeReferences
with one item -- System.Int32, because MyList implements/is assignable
to IEnumerable<System.Int32>.

For this I think I have to traverse all the interfaces that MyList
implements and also interfaces that super-types of MyList implement
(and all interfaces these interfaces implement..., but traversing the
type hierarchy is not the problem here). The problem is that when I
want to access a base class or the list of interfaces I have to use
Resolve (to get a TypeDefinition), but when I use Resolve on generic
type instance e. g. List[[System.Int32, ...]] it resolves such type
into e. g. List[[T, ...]] so the actual type of the generic parameter
is lost. I think I have some insight on why this happens, but is there
any way how I could implement my extension method?

Steves

Jb Evain

unread,
Apr 28, 2011, 7:10:01 AM4/28/11
to mono-...@googlegroups.com
Hi Steves,

On Wed, Apr 27, 2011 at 6:10 PM, Steves <m...@stevesindelar.cz> wrote:
> but is there
> any way how I could implement my extension method?

You just have to memorize what arguments are used to instantiate the
generics instances, and apply them on top of the definitions.

Steves

unread,
Apr 30, 2011, 5:46:53 AM4/30/11
to mono-cecil
Hi Jb Evain,

I thought that, thank you for your affirmation.

I am missing one thing to implement this: when I have a reference to
say List<int>, I can get System.Int32 through
((GenericInstanceType)myReference).GenericArguments. How can I get the
information that System.Int32 is actually generic parameter "T"? In
debugger I can see that myReference has property
Mono.Cecil.IGenericContext.Type, which contains the GenericParameter
"T", but the Mono.Cecil.IGenericContext interface is internal.

Steves

Jb Evain

unread,
Apr 30, 2011, 5:51:45 AM4/30/11
to mono-...@googlegroups.com
On Sat, Apr 30, 2011 at 11:46 AM, Steves <m...@stevesindelar.cz> wrote:
> I am missing one thing to implement this: when I have a reference to
> say List<int>, I can get System.Int32 through
> ((GenericInstanceType)myReference).GenericArguments. How can I get the
> information that System.Int32 is actually generic parameter "T"? In
> debugger I can see that myReference has property
> Mono.Cecil.IGenericContext.Type, which contains the GenericParameter
> "T", but the Mono.Cecil.IGenericContext interface is internal.

You can write:

var list_int32_ref = (GenericInstanceType) myReference;
var int32_ref = genericInstance.GenericArguments [0];
var list_ref = genericInstance.ElementType;
var t = list_ref.GenericParameters [0];

Jb

Steves

unread,
May 5, 2011, 8:09:49 AM5/5/11
to mono-cecil
Tank you Jb Evain, with this I almost managed to implement it, but I
come across another thing that confuses me:

Consider this hierarchy:
class StrDict<T> : Dictionary<string, T>
class MyDictionary : StrDict<double>

and following code:
var strDictRef = myDictionaryRef.Resolve().BaseType;
var strDictPars =
((GenericInstanceType)strDictRef).ElementType.GenericParameters;
var dictRef = strDictRef.Resolve().BaseType; // this is
reference to Dictionary<string, T>
var dictPars =
((GenericInstanceType)dictRef).ElementType.GenericParameters;

strDictPars contain, as expected, a generic parameter with name 'T',
so I can do the mapping T -> double.
So far so good. However, dictPars contain generic parameters with
names '!0' and '!1' instead of TKey and TValue,
so I cannot map generic arguments of the Dictionary<string, T>
reference, which are string and double (got from T -> double),
to IDictionary<TKey, TValue>. Is this by design, or a bug? How can I
get correct names of the
generic parameters in this case? (Btw. I am using the latest NuGet
package and .NET 4.0).

Thank you for your patience Jb O:-)

Jb Evain

unread,
May 5, 2011, 8:13:18 AM5/5/11
to mono-...@googlegroups.com
Hi,

On Thu, May 5, 2011 at 2:09 PM, Steves <m...@stevesindelar.cz> wrote:
> So far so good. However, dictPars contain generic parameters with
> names '!0' and '!1' instead of TKey and TValue,
> so I cannot map generic arguments of the Dictionary<string, T>
> reference, which are string and double (got from T -> double),
> to IDictionary<TKey, TValue>. Is this by design, or a bug?

This is by design, in the metadata, generic arguments for references
are only serialized by position, not by name.

> How can I
> get correct names of the
> generic parameters in this case?

You have to Resolve the ElementType to get the definition, which will
have the named generic parameters that you can address by position.

Jb

Steves

unread,
May 5, 2011, 1:36:39 PM5/5/11
to mono-cecil
Many thanks, that worked like a charm.

In case someone is interested in resulted implementation, the code can
be found here:
http://blog.stevesindelar.cz/mono-cecil-how-to-get-all-base-types-and-interfaces-with-resolved-generic-arguments

Ahmed el-Sawalhy

unread,
Aug 4, 2017, 1:24:49 PM8/4/17
to mono-cecil
Although the link is now expired, I managed to access the content through Google Cache. Thank you very much, Steves, as your code helped me solve an issue that frustrated me for days :).
Reply all
Reply to author
Forward
0 new messages