What is the difference between Type.IsGenericType
and Type.IsGenericTypeDefinition
? Interestingly enough, MSDN's link for IsGenericTypeDefinition is broken.
Update: IsGenericTypeDefinition MSDN's entry
After playing a bit with trying to retrieve all the DbSets defined in a given DbContext, I was lead to the following, which behavior I am trying to understand: filtering properties via IsGenericType returns the desired results, while with IsGenericTypeDefinition not (does not return any).
It's interesting that from this post I have the impression that the author did get his DbSets using IsGenericTypeDefinition, while I did not.
Follows a sample that illustrates the discussion:
private static void Main(string[] args)
{
A a = new A();
int propertyCount = a.GetType().GetProperties().Where(p => p.PropertyType.IsGenericType).Count();
int propertyCount2 = a.GetType().GetProperties().Where(p => p.PropertyType.IsGenericTypeDefinition).Count();
Console.WriteLine("count1: {0} count2: {1}", propertyCount, propertyCount2);
}
// Output: count1: 1 count2: 0
public class A
{
public string aaa { get; set; }
public List<int> myList { get; set; }
}
Use the IsGenericType property to determine whether a Type object represents a generic type. Use the ContainsGenericParameters property to determine whether a Type object represents an open constructed type or a closed constructed type. The IsGenericType property returns false if the immediate type is not generic.
IsGenericType tells you that this instance of System. Type represents a generic type with all its type parameters specified. For example, List<int> is a generic type. IsGenericTypeDefinition , on the other hand, tells you that this instance of System.
IsGenericType
tells you that this instance of System.Type
represents a generic type with all its type parameters specified. For example, List<int>
is a generic type.
IsGenericTypeDefinition
, on the other hand, tells you that this instance of System.Type
represents a definition from which generic types can be constructed by supplying type arguments for its type parameters. For example, List<>
is a generic type definition.
You can get a generic type definition of a generic type by calling GetGenericTypeDefinition
:
var listInt = typeof(List<int>);
var typeDef = listInt.GetGenericTypeDefinition(); // gives typeof(List<>)
You can make a generic type from a generic type definition by providing it with type arguments to MakeGenericType
:
var listDef = typeof(List<>);
var listStr = listDef.MakeGenericType(typeof(string));
(This answer compares all of the generic-type-related properties of Type
in a side-by-side table below, so if you're already familiar with .NET's generics and just want a reference then just scroll down to the table)
First, remember the difference between parameters and arguments, especially w.r.t. generic type parameters and generic type arguments (and also generic method type parameters and generic method type arguments):
class Generic<T0,T1> {}
, the T0
and T1
symbols are the generic type parameters. Note that when simply given a generic class
definition that's unused then there's no type arguments.Generic<String,Object> gen = new Generic<String,Object>
then...
T0
is String
.T1
is Object
.class Generic<TItem> { public Object Foo() { return new List<TItem>(); } }
Foo
method) the class Generic<TItem>
's generic type parameter TItem
is used as the generic type argument for List<T>
's generic type parameter T
.NotGenericClass
in class NotGenericClass { void GenericMethod<T>() { } }
does not have any generic type parameters, but its method GenericMethod<T>()
does have a single generic method type parameter - and if GenericMethod
is never ever actually called/used/invoked then GenericMethod
will not have any generic method type arguments as those only exist at generic instantiation sites (i.e. at the point of generic instantiation).Given these C# classes...
class NormalClass { }
class Generic<T> { }
class Derived : Generic<String> { }
class HasGenericMethod { public void Foo<T>() {} }
...and these Type
instances from GetGenericArguments()
:
Type[] genericTypeArgs = typeof(Generic<>).GetGenericArguments();
Type genTypeArg = genericTypeArgs.Single();
Type[] genericMethodTypeArgs = typeof(HasGenericMethod).GetMethod( nameof(HasGenericMethod.Foo) ).GetGenericArguments();
Type genMethodArg = genericMethodTypeArgs.Single();
...then their typeof()
expressions will have these properties:
Example | typeof(NormalClass) |
typeof(Generic<>) |
typeof(Generic<String>) |
typeof(Derived) |
genTypeArg |
genMethodArg |
typeof(Generic<String>[]) |
---|---|---|---|---|---|---|---|
Type properties | |||||||
Type.IsTypeDefinition |
Yes | Yes | No | Yes | No | No | No |
Type.IsGenericType |
No | Yes | Yes | No | No | No | No5 |
Type.ContainsGenericParameters |
No | Yes | No | No | Yes4 | Yes4 | No |
Type.GenericTypeArguments |
Empty | Empty | { typeof(String) } |
Empty | Empty | Empty | Empty |
Type.IsConstructedGenericType |
No | No | Yes | No | No | No | No |
Type.IsGenericTypeDefinition |
No | Yes | No | No | No | No | No |
Generic parameter properties: | |||||||
Type.IsGenericParameter |
No | No | No | No | Yes | Yes | No |
Type.IsGenericMethodParameter |
No | No | No | No | No | Yes | No |
Type.IsGenericTypeParameter |
No | No | No | No | Yes | No | No |
Methods: | |||||||
Type.GetGenericArguments() |
Empty | { typeof(T) } |
{ typeof(String) } |
Empty | Empty | Empty | { typeof(String) } |
Type.GetGenericParameterConstraints() |
Exception1 | Exception1 | Exception1 | Exception1 | Empty | Empty | Exception1 |
Type.GetGenericTypeDefinition() |
Exception2 | typeof(Generic<>) |
typeof(Generic<>) |
Exception2 | Exception2 | Exception2 | Exception2 |
You can generate this table yourself (albiet, transposed) using this LinqPad script.
As a reminder to myself: if you have a Type
object from Object.GetType()
for an object that may be either a closed generic type (i.e. Object.GetType().IsConstructedGenericType == true
), or is a non-generic type derived from that generic type, and you want to find out what, do this:
private static readonly Type _knownGenericType = typeof(Generic<>);
public static Boolean TryGetTypeArgsOfKnownGenericType( Object obj, [NotNullWhen(true)] out Type? actualArgType )
{
Type t = obj.GetType();
while( t != null )
{
if t.IsConstructedGenericType && t.GetGenericTypeDefinition() == _knownGenericType )
{
Type[] tArgs = t.GetGenericArguments();
actualArgType = tArgs.Single();
return true;
}
t = t.BaseType;
}
actualArgType = null;
return false;
}
So this code below will print "Sucess: T := System.String" twice:
if( TryGetTypeArgsOfKnownGenericType( new Derived(), out Type? tArg ) )
{
Console.WriteLine("Success: T := " + tArg.FullName);
}
if( TryGetTypeArgsOfKnownGenericType( new Generic<String>(), out Type? tArg ) )
{
Console.WriteLine("Success: T := " + tArg.FullName);
}
Footnotes:
InvalidOperationException
: "Method may only be called on a Type for which Type.IsGenericParameter
is true."
InvalidOperationException
: "This operation is only valid on generic types."
typeof(T)
is typeof(Generic<>).GetGenericArguments().Single()
It's surprising that typeof(T).ContainsGenericParameters == true
when T
is a generic type parameter without an argument set (i.e. T
is undefined), so I'd have expected an InvalidOperationException
to be thrown instead.
ContainsGenericParameters
seemingly justifies returning true
(emphasis mine):
For convenience and to reduce the chance of error, the
ContainsGenericParameters
property provides a standard way to distinguish between closed constructed types, which can be instantiated, and open constructed types, which cannot. If theContainsGenericParameters
property returnstrue
, the type cannot be instantiated.
Apparently using typeof(T[])
when T
is a constructed generic type: the ContainsGenericParameters
property is false but the GetGenericArguments()
method returns a non-empty array of the type-arguments of T
instead of the type-arguments of System.Array
(which isn't actually a generic type).
typeof(Generic<String>[]).IsGenericType == false
typeof(Generic<String>[]).GetGenericArguments() == new[] { typeof(String) }
The
ContainsGenericParameters
property searches recursively for type parameters. For example, it returnstrue
for an array whose elements are typeA<T>
even though the array is not itself generic. Contrast this with the behavior of theIsGenericType
property, which returnsfalse
for arrays.
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