tags:

views:

729

answers:

4

I was going through Asp.Net MVC lesson and learned that, for a method to qualify as an action for a controller,

  • It must not have an "open genric type"

I understand generics some how and use them as well to some extent, but

  • what is an open generic type in .Net.
  • Is there something as closed generic type ?
  • open genric type is a term not used very often. What is used / confused with it ?

Thanks

+22  A: 

The C# language defines an open type to be a type that's either a type argument or a generic type defined with unknown type arguments:

All types can be classified as either open types or closed types. An open type is a type that involves type parameters. More specifically:

  • A type parameter defines an open type.
  • An array type is an open type if and only if its element type is an open type.
  • A constructed type is an open type if and only if one or more of its type arguments is an open type. A constructed nested type is an open type if and only if one or more of its type arguments or the type arguments of its containing type(s) is an open type.

A closed type is a type that is not an open type.

Therefore T, List<T>, and Dictionary<string,T>, and Dictionary<T,U> are all open types (T and U are type arguments) whereas List<int> and Dictionary<string,int> are closed types.

There's a related concept: An unbound generic type is a generic type with unspecified type arguments. An unbound type can't be used in expressions other than typeof() and you can't instantiate it or call its methods. For instance, List<> and Dictionary<,> are unbound types.

To clarify the subtle distinction between an open type and an unbound type:

class Program {
   static void Main() { Test<int>(); }
   static void Test<T>() {
      Console.WriteLine(typeof(List<T>)); // Print out the type name
   }
}

If you run this snippet, it'll print out

System.Collections.Generic.List`1[System.Int32]

which is the CLR name for List<int>. It's clear at runtime that the type argument is System.Int32. This makes List<T> a bound open type.

At runtime, you can use reflection to bind type arguments to unspecified type parameters of unbound generic types with the Type.MakeGenericType method:

Type unboundGenericList = typeof(List<>);
Type listOfInt = unboundGenericList.MakeGenericType(typeof(int));
if (listOfInt == typeof(List<int>))
     Console.WriteLine("Constructed a List<int> type.");

You can check whether a type is an unbound generic type (generic type definition) from which you can construct bound types with the Type.IsGenericTypeDefinition property:

Console.WriteLine(typeof(Dictionary<,>).IsGenericTypeDefinition); // True
Console.WriteLine(typeof(Dictionary<int,int>).IsGenericTypeDefinition); // False

To get the unbound type from a constructed type at runtime, you can use the Type.GetGenericTypeDefinition method.

Type listOfInt = typeof(List<int>);
Type list = listOfInt.GetGenericTypeDefinition(); // == typeof(List<>)

Note that for a generic type, you can either have a completely unbound type definition, or a completely bound definition. You can't bind some type parameters and leave others unbound. For instance, you can't have Dictionary<int,> or Dictionary<,string>.

Mehrdad Afshari
+1 great info - I learned something new today. I knew that List<> was a generic type, but now I know the correct technical term.
dboarman
You *can* actually partially close a generic type by providing a type argument that is itself an open generic type. However, this is a dead end. The framework doesn't officially recognize this partial state, treating it as neither closed nor open, and so it doesn't allow you to do anything useful with it.
Chris Ammerman
+1  A: 

An "open generic type" is just a generic type that doesn't yet have its type specified (e.g., CargoCrate<T>). It becomes "closed" once a concrete type has been assigned (e.g. CargoCrate<Widget>).

For example, say you have something like this:

public class Basket<T> {
  T[] basketItems;
}

public class PicnicBlanket<T> {
  Basket<T> picnicBasket;   // Open type here. We don't know what T is.
}

                                 // Closed type here: T is Food.
public class ParkPicnicBlanket : PicnicBlanket<Food> {
}

Here, picnicBasket's type is open: nothing's yet been assigned to T. When you make a concrete PicnicBlanket with a specific type -- for example, by writing PicnicBlanket<Food> p = new PicnicBlanket<Food>() -- we now call it closed.

John Feminella
+3  A: 

Just to add:

Dictionary<string, T> (or more precisely Dictionary<string,>) is still an open type.

Example:

void Foo<T>(Dictionary<string,T> dic) { ... }
leppie
@Mehrdad Afshari: Sorry, but you can still get what you call semi-closed types...
leppie
Also, I have no idea how to construct that (or represent the type), might be a worthwhile question.
leppie
@Mehrdad Afshari: looks pretty open to me, but I am open to suggestions too. :)
leppie
@leppie: Upon looking up the spec, I'm seeing that you're right. I thought the "generic type definition" is the same thing as "open type" in the spec but apparently, the spec calls a type parameter and `List<T>` open types and the `List<>` an unbound type. Clarifying my answer. +1
Mehrdad Afshari
+1  A: 

There are three kinds of generic types. To make it short, in this (simplified) declaration:

public class Dictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
  • Dictionary<TKey, TValue> is an unbounded generic type.

  • KeyValuePair<TKey, TValue> is, in this case, an open constructed generic type. It has some type parameters, but they are already defined elsewhere (in Dictionary, in this case).

  • Dictionary<string, int> would be a closed constructed generic type.

Gorpik