• Feeds

    Subscribe in a reader

  • Ads

Reflecting over generic types

I wrote my "hello, world" program for .NET 2.0. I wanted to see how reflection worked with generic types, so I wrote some code that reverses a dictionary. That is, it takes a Dictionary that maps keys to values and returns a Dictionary whose keys are the original values and the values are the original keys.

First off, some definitions: Generic types come in two flavors, open and closed. An open generic type is what you get when your write a definition for a generic type or a generic method. An open generic has as part of its definition placeholders for types that will get filled in later when the type is instantiated (much like the way a regular method has some parameters whose values get filled in when that method is actually called). A closed generic type is an instantiation of an open generic type, which is what you get when you actually bind values to an open generic. It's what you get when you declare a variable to be a List -- List is a closed generic type. Open generics are a sort of metaclass; open generics get instantiated into closed generics which get instantiated into objects.

Ok, enough theory. There's some significant additions to the Reflection API's to support generics in Whidbey. First off, System.Type and System.Reflection.MethodInfo now support a property called HasGenericParameters, which returns true if a type or method is generic. There's also a related property called HasUnboundGenericParameters which will tell you if a type is an open or closed generic.

The only way you can actually get your hands on an open generic type at runtime is to call the GetGenericTypeDefinition() method on the type of the closed generic. This is how you can convert from typeof( Dictionary ) to typeof( Dictionary ).

Given a closed generic type, you can determine the types to which the generic parameters are bound by calling the GetGenericParameters() method. This gives you back an array of System.Type instances that are positionally related to the types of the parameters. So if you do a GetGenericParameters on typeof( SomeClass) you'll get back an type array of

{ typeof( int ), typeof( string ), typeof( float ) }.

You can convert an open generic to a closed generic by calling BindGenericParameters() and passing it a type array. Like GetGenericParameters, this array is parallel to the generic parameter list. Thus, you can convert a typeof( SomeClass< T, U, V > ) to a typeof( SomeClass< int, string, float > ) by calling BindGenericParameters( new Type[]{ typeof(int), typeof(string), typeof(float) } ).

Once you have a closed generic type hanging around, you can get an instance of that closed generic in the normal reflection way -- by calling Activator.CreateInstance(). Nothing magical here.

There are a couple of other methods hanging around System.Type that have to do with generics, but I didn't have any immediate use for them.

In summary:

   -- System.Type and System.MethodInfo now have a HasGenericParameters property
   -- You can convert can convert a closed generic to an open generic by calling GetGenericTypeDefinition()
   -- You can convert an open generic to a closed generic by calling BindGenericParameters()
    -- You can instantiate a closed generic with Activator.CreateInstance()

Here's the code for ReverseDictionary():

public static Dictionary ReverseDictionary( Dictionary original )
{
   Type originalType = original.GetType();
  
Type openOriginal = originalType.GetGenericTypeDefinition();
  

   Type[] typeParameters = originalType.GetGenericParameters();
   Debug.Assert( typeParameters.Length == 2 );

   Type keyType = typeParameters[0];
  
Debug.Assert( false == keyType.IsGenericParameter );

  
Type valueType = typeParameters[1];
  
Debug.Assert( false == valueType.IsGenericParameter );

  
Type reverseDictType = openOriginal.BindGenericParameters( new Type[]{ valueType, keyType } );
  
Debug.Assert( true == reverseDictType.HasGenericParameters );
  
Debug.Assert( false == reverseDictType.HasUnboundGenericParameters );

   Dictionary reverse = (Dictionary)Activator.CreateInstance( reverseDictType );

   //We could have just said
  
//Dictionary reverse = new Dictionary()
  
//but that wouldn't have made for a very fun demo...

   foreach (K key in original.Keys)
  
{
    
V val = original[key];
    
reverse[val] = key;
   
}

  
return reverse;
}

 

GenericReflection.cs.txt (2.37 KB)