What exactly is an open generic type in .NET?
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>
.
Generics open and closed constructed types
In practice the terminology doesn't really matter much - I can't remember the last time I had to worry about it except when trying to write about it.
- An unbound type has no type arguments specified
- A constructed type has at least one type argument specified
- A type parameter is an open type
- An array type where the element type is open is an open type
- An open constructed type has at least one type argument which is an open type
- A closed type is any type which isn't open
(There are further rules for nested types. Consult the C# 3.0 spec section 4.4 for gory details.)
As an example of an open constructed type, consider:
public class NameDictionary<T> : Dictionary<string, T>
The base class of typeof(NameDictionary<>)
is:
- Constructed because it specifies type arguments
- Open because the second type argument (
T
) is an open type
The MSDN docs for Type.IsGenericType
have quite a useful little table.
Just to reiterate, this is almost entirely unimportant in day to day use.
I'm generally in favour of knowing the correct terminology - particularly for things like "pass by reference" etc - but in this case it really, really doesn't come up very often. I would like to actively discourage you from worrying about it :)
Registercollection open generic type
To be able to achieve what you want, you will have to make your IFoo<T>
interface variant.
The reason you get an empty list is because IFoo<IBar>
is not assignable from IFoo<FooBar>
. Try it out yourself: the C# compiler will not permit you to cast Foo
to IFoo<IBar>
.
To achieve this you will have to make IFoo<T>
covariant. In other words, you need to define the interface as follows:
public interface IFoo<out T> { }
After you done this, you'll see that Simple Injector automatically resolves all instances for you as you'd expect.
Whether however it is useful for you to have an out
type argument is a different question. One that I cannot answer, given the abstract description you gave.
.NET generics terminology - open/closed, unbound/constructed
From the language specification:
4.4 Constructed types
A generic type declaration, by itself,
denotes an unbound generic type that
is used as a “blueprint” to form many
different types, by way of applying
type arguments. The type arguments are
written within angle brackets (< and> )
immediately following the name of the generic type. A type that includes
at least one type argument is called a
constructed type. A constructed type
can be used in most places in the
language in which a type name can
appear. An unbound generic type can
only be used within a
typeof-expression (§7.6.11).
[...]4.4.2 Open and closed types
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.
[...]4.4.3 Bound and unbound types
The term unbound type refers to a non-generic
type or an unbound generic type. The
term bound type refers to a
non-generic type or a constructed
type. An unbound type refers to the
entity declared by a type declaration.
An unbound generic type is not itself
a type, and cannot be used as the type
of a variable, argument or return
value, or as a base type. The only
construct in which an unbound generic
type can be referenced is the typeof
expression (§7.6.11).
Here's an example I thought of:
// Foo<T> is an unbound generic type.
class Foo<T> { .. }
// Bar<K> is an unbound generic type.
// Its base-class Foo<K> is a constructed, open generic type.
class Bar<K> : Foo<K> { .. }
// IntFoo is not a generic type.
// Its base-class Foo<int> is a constructed, closed generic type.
class IntFoo : Foo<int> { .. }
And here's an attempt to tie that in with the reflection API, using the relevant properties: IsGenericType
, IsGenericTypeDefinition
and ContainsGenericParameters
(These tests are not 100% predictive of each "kind" as per the language spec).
+----------+---------------------+-----------+--------------+-------------------+
| Name | Kind | IsGenType | IsGenTypeDef | ContainsGenParams |
+----------+---------------------+-----------+--------------+-------------------+
| Foo<> | Unbound | TRUE | TRUE | TRUE |
| Foo<>* | Constructed, open | TRUE | FALSE | TRUE |
| Foo<int> | Constructed, closed | TRUE | FALSE | FALSE |
| IntFoo | Not generic | FALSE | FALSE | FALSE |
+----------+---------------------+-----------+--------------+-------------------+
* = Bar<>'s base type.
C# Language: How to get type of bound but open Generic class?
What you are proposing to do is impossible and also will not really help you.
Impossible because...
The documentation states (emphasis mine) that
Types constructed with MakeGenericType can be open, that is, some of
their type arguments can be type parameters of enclosing generic
methods or types.
This means that you cannot make a Type
object representing XClass<int,>
. What you can do is:
class Outer<TOuter>
{
class XClass<T, U> {}
}
In this situation, you can make a Type
object representing Outer<TOuter>.XClass<int, TOuter>
. But there needs to be an enclosing generic class.
Not useful because...
The documentation also states (referring to a similar example to the above) that:
A constructed type such as Base is useful when emitting code,
but you cannot call the MakeGenericType method on this type because it
is not a generic type definition. To create a closed constructed type
that can be instantiated, first call the GetGenericTypeDefinition
method to get a Type object representing the generic type definition
and then call MakeGenericType with the desired type arguments.
Which means that if you have
Type myType = ... // represents Outer<TOuter>.XClass<int, TOuter>
Then to get a Type
for XClass<int, string>
you would first need to call myType.GetGenericTypeDefinition()
(thus losing the int
information) and then call MakeGenericType
to put it back in (along with the string
type parameter). So it's like one step back and two steps forward.
Alternatives
You might want to consider storing the type parameter types for XClass
in a separate data structure (e.g. a Type[]
) for as long as not all type parameters are known to you, and then create the closed generic type in one go after you have collected all of them.
You can also package all this into a small helper class for convenience:
class GenericTypeDescription
{
private readonly Type openGenericType;
private readonly Type[] typeParameters;
public GenericTypeDescription(Type openGenericType)
{
// add checks for openGenericType actually being what it says here
this.openGenericType = openGenericType;
this.typeParameters = new Type[openGenericType.GetGenericArguments().Length];
}
public void SetTypeParameter(int index, Type type) {
// add error handling to taste
this.typeParameters[index] = type;
}
public Type ConstructGenericType() {
// add error handling to taste
return this.openGenericType.MakeGenericType(this.typeParameters);
}
}
What is the open generic type of the array []?
It's just System.Array; it isn't generic. See here for a brief discussion: http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx
When is the generic type resolved in c#?
The problem is that the question is not well-posed. Two people are claiming opposite things: that types are "resolved" at runtime and that types are "resolved" at compile time.
Since they are contradicting each other, they must both mean something different by "resolved".
I do not know what it means for a type to be "resolved". I do know however what overload resolution is. When asked to solve an overload resolution problem that does not involve dynamic
, the C# compiler determines which overload to call at compile time, based on the compile time information about the generic type. So for example, if you have:
static void Main()
{
var d = new D();
var p = new P<D>();
p.N(d);//Displays In class B
}
class B
{
public void M()// Note, not virtual
{
Console.WriteLine("In class B");
}
}
class D : B
{
public new void M()// new, not overload
{
Console.WriteLine("In class D");
}
}
class P<T> where T : B
{
public void N(T t)
{
t.M();
}
}
N
always calls B.M
even if P<T>
is instantiated as P<D>
. Why? Because the overload resolution problem that determines what the meaning of t.M
is must be solved when P<T>.N
is compiled, and at that time, the best the compiler knows is that t
must be B
, so it chooses B.M
.
If that's not what you mean by "resolved" then clarify the question.
Registering a closed generic class implementation with an opened generic interface
You should register your repository as closed generic interface:
services.AddScoped<IConcreteRepository<Department, int>, DepartmentRepository>();
Related Topics
Difference Between Covariance & Contra-Variance
Make a Borderless Form Movable
Get the Correct Week Number of a Given Date
Reading an Integer from User Input
How to Perform a Left Outer Join Using Linq Extension Methods
How to Get C# Enum Description from Value
Why Would You Use String.Equals Over ==
How to Remove All Namespaces from Xml With C#
Json.Net Parser *Seems* to Be Double Serializing My Objects
Sockets on Same MAChine for Windows and Linux
Cross-Platform File Name Handling in .Net Core
How to Compile a Windows.Forms Application on Linux for .Net Core
Filling Missing Dates by Group