What exactly is an "open generic type" in .NET? [duplicate]
Asked Answered
U

4

149

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 generic type"

I understand generics somewhat and use them to some extent, but:

  • What is an open generic type in .Net.
  • Is there such a thing as a closed generic type?
  • Open generic type is a term not used very often. What is used / confused with it ?
Ubiquitarian answered 31/1, 2010 at 20:2 Comment(0)
A
241

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>.

Aeschylus answered 31/1, 2010 at 20:6 Comment(5)
+1 great info - I learned something new today. I knew that List<> was a generic type, but now I know the correct technical term.Eudo
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.Pasol
great explanation as to difference between open type and unbound type - didn't know before!Absurd
I agree with @ChrisAmmerman. For example the type Dictionary<int,> that you claim we can't have, is more or less constructed by typeof(Dictionary<,>).MakeGenericType(typeof(int), typeof(Dictionary<,>).GetGenericArguments()[1]). But maybe you just meant to say that C# does not allow the syntax Dictionary<int,> so we have to create it manually like I did here.Fed
... And on the doc page for IsGenericType, in the Examples section's C# code, they regard in a more elegant way a type Base`2[System.String,V] where one type parameter is substituted with a concrete type and another one is substituted with a parameter V.Fed
R
18

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.

Roccoroch answered 31/1, 2010 at 20:8 Comment(0)
G
14

Just to add:

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

Example:

void Foo<T>(Dictionary<string,T> dic) { ... }
Gombosi answered 31/1, 2010 at 20:29 Comment(1)
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. +1Aeschylus
G
11

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.

Guncotton answered 3/2, 2010 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.