Consider a piece of the code below:
public class A<T> { }
public class B<T> : A<T> { }
In such case:
var a = typeof(A<>).GenericTypeArguments.Length;
a
has the value 0
, which is not surprising. This, however, is somehow unexpected for me:
var b = typeof(B<>).BaseType.GenericTypeArguments.Length;
where b
has the value 1
. So it is closed using a non-existing type of name "T" and only doing GetGenericTypeDefinition
on it makes it open again. Why is that?
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.
As noted in the Subsequent Procedures Policy Development Process Working Group (SubPro PDP WG) Final Report, a closed generic is "a TLD representing a string that is a generic name or term under which domains are registered and usable exclusively by the registry operator or its affiliates."
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged 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 (§11.7. 16).
So it is closed using a non-existing type of name "T" and only doing GetGenericTypeArgument on it makes it open again. Why is that?
Because there is one type argument provided - the type parameter to B
.
Look at how you're specifying the base class:
public class B<T> : A<T>
What's the T
in A<T>
if it's not a type argument? Just because the type argument is itself a type parameter doesn't mean it's not being specified as a type argument.
Consider this:
public class A<T1, T2> { }
public class B<T> : A<T, int> { }
Here, the base class of B<T>
is A<T, int>
- and you can determine that the int
has been specified by asking for the type arguments. You can also show where the T
comes from:
using System;
using System.Reflection;
using System.Collections.Generic;
public class A<T1, T2> { }
public class B<T> : A<T, int> { }
class Program
{
static void Main()
{
var bT = typeof(B<>).GetTypeInfo().GenericTypeParameters[0];
var listT = typeof(List<>).GetTypeInfo().GenericTypeParameters[0];
var bBaseArguments = typeof(B<>).BaseType.GenericTypeArguments;
Console.WriteLine(bBaseArguments[0] == bT); // True
// Shows that the T from B<T> isn't the same as the T from List<T>
Console.WriteLine(bBaseArguments[0] == listT); // False
Console.WriteLine(bBaseArguments[1] == typeof(int)); // True
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With