Welcome to MSDN Blogs Sign in | Join | Help

Generics Terminology in .NET Framework

Hi Folks...

The other day I was investigating an issue that finally boiled down to incorrect usage of System.Reflection & System.Reflection.Emit APIs (SR & SRE hereafter) to analyze Generic types. We were using Type.IsGenericParameter instead of using Type.IsGenericType. During investigation, what made things worse is that each time I went through the first paragraph of their respective help pages, I got more confused. Then I decided to read the Remarks section of the help pages and I was hopelessly lost because it seemed to be written in a dialect of English that I didn’t speak! 

Do you know the difference between Type.IsGenericParameter and Type.IsGenericType? The difference between Generic Type Parameter and Generic Type Argument perhaps? How about Closed Constructed Generic Method and Open Constructed Generic Method? These are a part of some subtle terminology introduced into .NET Framework with Generics. Using SR & SRE without a clear understanding this terminology is only going to land you into trouble. You are going to introduce bugs into your code and you will have a really hard time debugging your own code! Just like me!

It is all there in MSDN but it is highly confusing at the first glance! So I decided to get done with this for good. I spent a good 3 hours going over Generics terminology in .NET Framework in MSDN. This is a gist of my understanding. I am all clear now. You should be too before you begin using SR and SRE.

Basic Terminology

  • Generics:
    • They are classes/structs/interfaces/methods (CSIM hereafter) that have placeholders for one or more of the types they store and/or use.
  • Generic Type:
    • Union of the set of all Generic Type Definitions and Constructed Types.
    • A System.Type object represents a Generic Type iff its IsGenericType property is true
  • Generic Method:
    • Must have a non-empty list of Generic Type Parameters.
    • A method being a Generic Method doesn’t have anything to do with the enclosing type being Generic Type or not
    • Union of the set of all Generic Method Definitions and Constructed Methods.
    • Generic Type Parameters can appear as the return type or as the types of the formal parameters
    • A System.Reflection.MethodInfo object represents a Generic Method iff its IsGenericMethod property is true
  • Generic Type Definition:
    • A CSI declaration that serves as a template, with placeholders for the types it can contain or use.
    • A System.Type object represents a Generic Type Definition iff its IsGenericTypeDefinition property is true
  • Generic Method Definition:
    • A method with two parameter lists: A non-empty list of placeholder types and a list of formal parameters
    • A System.Reflection.MethodInfo object represents a Generic Method Definition iff its IsGenericMethodDefinition property is true
  • Generic Type Parameter:
    • It is the placeholder type in a Generic Type/Method Definition.
    • It is represented using a System.Type object in SR and SRE.
    • A System.Type object represents a Generic Type Parameter iff its IsGenericParameter property is true
    • Example: Is T in Dictionary<T, int> in "class C<T> : Dictionary<T, int>" a Generic Type Parameter? Yes!
  • Generic Type Argument:
    • It is any type that is substituted for a Generic Type Parameter. It could be another Generic Type Parameter.
    • IsGenericParameter is false
    • A System.Type object represents a Generic Type Argument iff its IsGenericParameter property is false
  • Constructed Generic Type/Method:
    • A new type/method constructed as a result of specifying actual types for one or more of the Generic Type Parameters of a Generic Type/Method Definition.
    • Example: Is A<B<, >, > a Constructed Generic Type? Yes!
  • Closed/Open Constructed Generic Type:
    • A Closed Constructed Generic Type is the result of specifying Generic Type Arguments for all Generic Type Parameters of a Generic Type/Method Definition. An Open Constructed Generic Type otherwise.
    • A System.Type object represents a Closed Constructed Generic Type iff its ContainsGenericParameters property is false. Otherwise it is an Open Constructed Generic Type.
    • Only a Closed Constructed Generic Type can be instantiated
    • Is A<B<T, string>, int>.C a Closed Constructed Generic Type? No!
  • Closed/Open Constructed Generic Method:
    • A Closed Constructed Generic Method has no unassigned Generic Type Parameters *and* the containing type is a Closed Constructed Generic Type *and* all the Generic Type Arguments are Closed Constructed Generic Types. An Open Constructed Generic Method otherwise.
    • A System.Reflection.MethodInfo object represents a Closed Constructed Generic Method iff its ContainsGenericParameters property is false. Otherwise it is an Open Constructed Generic Method
  • Constraints:
    • They are limits placed on Generic Type Parameters.
    • The GenericParameterAttributes property of a System.Type object representing a Generic Type Parameter gets a combination of System.Reflection.GenericParameterAttributes flags that describe its covariance and special constraints
    • The GetGenericParameterConstraints() method of a System.Type object representing a Generic Type Parameter returns an array of System.Type objects that represent its constraints.

A few points to note

  • If a nested types doesn't have Generic Type Parameters of its own, the enclosing type determines the above for it.
  • Generic Type Parameters and Generic Type Arguments have the same relation as the parameters and arguments of a function.
  • typeof(Dictionary<, >).MakeArrayType() returns a System.Type object that is not a Generic Type but is an Open Constructed Generic Type. Same for pointers
  • In SR & SRE:
    • Generic Type Parameters are represented by System.Type
    • A Type/MethodInfo object representing a Generic Type/Method has an array of types containing the Generic Type Parameters and Generic Type Arguments

Examples to consolidate our understanding

Consider the followign code snippet. The invariant conditions are shown in the comments below it.

public class Base<T, U>
{
    public static T M1Base(U u) { return default(T); }
}

public
class Derived<V> : Base<string, V>
{
    public G<Derived<V>> F;
   
    public
class Nested
    {
        void M1Nested() { }
    }
   
    public
static void M1Derived<W>() { }
}

public
class G<T> { }

/*
Type: Derived<V> 
                 IsGenericType: True
       IsGenericTypeDefinition: True
     ContainsGenericParameters: True
            IsGenericParameter: False

Type: Base<string, V>
                 IsGenericType: True
       IsGenericTypeDefinition: False
     ContainsGenericParameters: True
            IsGenericParameter: False

Type: Array of Derived<int>
                 IsGenericType: False
       IsGenericTypeDefinition: False
     ContainsGenericParameters: False
            IsGenericParameter: False

Type: Type parameter T in Base<T>
                 IsGenericType: False
       IsGenericTypeDefinition: False
     ContainsGenericParameters: True
IsGenericParameter: True

Type: Field Derived<V>.F
                 IsGenericType: True
       IsGenericTypeDefinition: False
     ContainsGenericParameters: True
IsGenericParameter: False

Type: Nested
                 IsGenericType: True
       IsGenericTypeDefinition: True
     ContainsGenericParameters: True
            IsGenericParameter: False

Method: T Base<T, U>.M1Base(U u)
               IsGenericMethod: False
     IsGenericMethodDefinition: False
     ContainsGenericParameters: True

Method: void Derived<V>.M1Derived<W>()
               IsGenericMethod: True
     IsGenericMethodDefinition: True
     ContainsGenericParameters: True

Method: void Derived<V>.M1Derived<int>();
               IsGenericMethod: True
     IsGenericMethodDefinition: False
     ContainsGenericParameters: True

Method: void Derived<string>.M1Derived<int>()
               IsGenericMethod: True
     IsGenericMethodDefinition: False
     ContainsGenericParameters: False
*/

Some of the System.Reflection API for Generics

  • System.Type.*Generic* members:
    • [P] ContainsGenericParameters: True if the type is an Open Constructed Generic Type. False otherwise. 
    • [P] GenericParameterAttributes: Gets a combination of System.Reflection.GenericParameterAttributes flags that describe the covariance and special Constraints of a Generic Type Parameter
    • [P] GenericParameterPosition: Gets the position of the Generic Type Parameter in the Generic Type Parameter list of the Generic Type/Method that declared the parameter. 
    • [P] IsGenericParameter: True if the type represents a Generic Type Parameter. False otherwise. 
    • [P] IsGenericType: True if the type represents a Generic Type. False otherwise. 
    • [P] IsGenericTypeDefinition: True if the type represents a Generic Type Definition. False otherwise. 
    • [M] GetGenericArguments: Get the array of types representing Generic Type Parameter/Arguments of the type.
    • [M] GetGenericParameterConstraints: Returns an array of System.Type objects that represent the Constraints of a Generic Type
    • [M] GetGenericTypeDefinition: Gets the Generic Type Definition corresponding to the Generic Type.
    • [M] MakeGenericType: Construct Closed/Open Constructed Generic Type from the Generic Type Definition
  • System.Reflection.MethodInfo.*Generic* members:
    • [P] ContainsGenericParameters: True if the type is an Open Constructed Generic Method. False otherwise.
    • [P] IsGenericMethod: True if the MethodInfo represents a Generic Method. False otherwise.  
    • [P] IsGenericMethodDefinition: True if the MethodInfo represents a Generic Method Definition. False otherwise.  
    • [M] GetGenericArguments: Get the array of types representing Generic Type Parameter/Arguments of the MethodInfo
    • [M] GetGenericMethodDefinition: Gets the Generic Method Definition corresponding to the Generic Method
    • [M] MakeGenericMethod: Construct Closed/Open Constructed Generic Method from the Generic Method Definition

References

If you want to learn more about Reflection & Generics in .NET Framework here are some references:

Finally as an exercise:

Type.IsGenericType Property help page contains an error in the Example-Invariants table. Armed with the above knowledge find it out and let me know! :)

That's all for now... See you in my next post!

Published Friday, October 21, 2005 8:30 AM by parthopdas

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Generics Terminology in .NET Framework

Friday, October 21, 2005 1:05 PM by Stuart Ballard
Excellent article, it clarified a lot of terminology for me and should be required reading for anyone who wants to really understand Generics.

A couple of your definitions are contradictory though and probably need some corrections.

First: "typeof(Dictionary<, >).MakeArrayType() returns a System.Type object that is not a Generic Type but is an Open Constructed Generic Type."

But... "Generic Type: Union of the set of all Generic Type Definitions and Constructed Types."

So by that definition it's impossible to have something that's a Constructed Type but not a Generic Type. I suspect you meant "that is not a Generic Type Definition..." in the first quote.


Second: "IsGenericParameter is false" and "A System.Type object represents a Generic Type Argument iff its IsGenericParameter property is false"

Both of those seem clearly false to me. Surely a System.Type, regardless of its IsGenericParameter value, is only a Generic Type Argument if it's also being used in the context of being substituted for a Generic Type Parameter? By your definition, typeof(object) is *always* a Generic Type Argument even when it's not being used in the context of generics at all.

Even if that *was* what you intended, it still contradicts the previous line "Generic Type Argument: It is any type that is substituted for a Generic Type Parameter. It could be another Generic Type Parameter." If it's another Generic Type Parameter, IsGenericParameter will be true.

Basically, I don't think there's any way to characterize a type as a Generic Type Argument or not based on the type itself, any more than there's any way to characterize an Object as a method argument or not based on the object itself. It's the *usage* of the object (or type) that makes it an argument or not.


Anyway, as I said, fantastic article - the fact that I was even able to identify these errors is a testament to how much I learned reading the rest of it ;)

# re: Generics Terminology in .NET Framework

Thursday, October 27, 2005 3:24 PM by parthopdas
Hi Stuart, you have made some interesting observations…

Point #1: "typeof(Dictionary<, >).MakeArrayType() returns a System.Type object that is not a Generic Type but is an Open Constructed Generic Type."
The invariants for the Type returned by typeof(Dictionary<, >).MakeArrayType() are:
IsGenericType = false => Means this is not a generic type
ContainsGenericParameters = true => Means this type has Generic Type Parameters, meaning this is an Open Constructed Generic Type.
As you have rightly pointed out, this is a clear contradiction to the statement “Union of the set of all Generic Type Definitions and Constructed Types”. So I suppose arrays and pointers are a exception to this rule!

Point#2: What I have written is probably a little unclear. The correct way to think of Generic Type Parameters and Generic Type Arguments is to compare them with formal and actual parameters, respectively, of a function. And yes, this only makes sense in the context of Generics.

What do you think?

# re: Generics Terminology in .NET Framework

Friday, October 28, 2005 1:59 PM by Stuart Ballard
On #1: Arrays and pointers being exceptions sounds like a reasonable explanation of what's going on.

On #2: I agree that the right analogy is with formal and actual parameters of a function. But I think the original article is actually incorrect rather than just unclear.

a) You said that a Generic Type Argument must have IsGenericParameter false, which isn't true because the argument might be another parameter (eg class Foo<T> {List<T> myList;} - the second use of T is a Generic Type Argument that has IsGenericParameter == true)

b) You said that "A System.Type object represents a Generic Type Argument iff its IsGenericParameter property is false" which is false by counterexample: typeof(object).IsGenericParameter is false, but that isn't enough to make it a Generic Type Argument.

I think it would be valuable to update the article itself to correct these points: this is an excellent resource to point anyone to for an explanation of the terms used in generics, but if I'm going to point someone at an article for reference, I'd rather not have to add "but check the comments at the bottom for corrections" ;)

# re: Generics Terminology in .NET Framework

Tuesday, April 04, 2006 3:55 PM by Dave Dolan
Well thanks to both of you -- even your little polite argument there helped me understand this a bit better. I can now proceed with my code migration.  Gotta get that boxing and casting out! Because of you I can read MS-documentation(TM)(R)(C) with a degree of confidence.

# Partho s Weblog Generics Terminology in NET Framework | Quick Diets

# Partho s Weblog Generics Terminology in NET Framework | Insomnia Cure

# Partho s Weblog Generics Terminology in NET Framework | storage bench

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker