IsGenericType 和 IsGenericTypeDefinition 之间的区别
Difference between IsGenericType and IsGenericTypeDefinition
Type.IsGenericType
和 Type.IsGenericTypeDefinition
和有什么不一样?有趣的是,MSDN 的 IsGenericTypeDefinition link 被破坏了。
尝试检索在给定 DbContext 中定义的所有 DbSet 后,我得到了以下结果,我试图理解这种行为:通过 IsGenericType 过滤属性 returns 期望的结果,而 IsGenericTypeDefinition 不是(return 没有)。
有趣的是,从 this post 我的印象是作者确实使用 IsGenericTypeDefinition 获得了他的 DbSet,而我没有。
下面是说明讨论的示例:
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; }
}
IsGenericType
告诉您 System.Type
的这个实例代表了一个指定了所有类型参数的泛型类型。例如,List<int>
是泛型。
另一方面,IsGenericTypeDefinition
告诉您 System.Type
的这个实例表示一个定义,可以通过为其类型参数提供类型实参从中构造泛型类型。例如,List<>
是泛型类型定义。
您可以通过调用 GetGenericTypeDefinition
:
来获取泛型类型的泛型定义
var listInt = typeof(List<int>);
var typeDef = listInt.GetGenericTypeDefinition(); // gives typeof(List<>)
您可以通过向 MakeGenericType
:
提供类型参数来从泛型类型定义创建泛型类型
var listDef = typeof(List<>);
var listStr = listDef.MakeGenericType(typeof(string));
(此答案在下方并排 table 中比较了 Type
的所有泛型类型相关属性,因此如果您已经熟悉 .NET 的泛型并且只需要参考然后向下滚动到 table)
首先,记住参数和参数的区别,尤其是w.r.t。 通用类型参数和通用类型参数(以及通用方法类型参数和泛型方法类型参数):
- 通用类型参数是“开放”通用类型中声明的类型“占位符”。
- 例如,在
class Generic<T0,T1> {}
中,T0
和T1
符号是通用类型参数。请注意,当简单地给出一个未使用的通用 class
定义时,则没有 类型参数 .
- 一个泛型参数是泛型[=256=的消费者为泛型参数指定的类型标识符].
- 例如,在
Generic<String,Object> gen = new Generic<String,Object>
那么...
- ...泛型类型参数 for 泛型类型参数
T0
是 String
.
- ...泛型类型参数 for 泛型类型参数
T1
是 Object
.
- 但是,通用类型参数不需要是具体类型:它们可以是来自使用者上下文的通用类型参数。
- 例如,在
class Generic<TItem> { public Object Foo() { return new List<T>(); } }
- ...然后
Generic<TItem>
中的泛型参数TItem
被用作泛型参数T
in List<T>
在 Foo
方法内。
- 是的,如果您对所有这些感到困惑,请不要担心,因为这很正常。
- 最后,泛型方法类型参数和泛型方法类型参数与泛型类型参数 和 泛型类型参数 除了它们被限制在一个方法中:
- 例如
class NotGeneric { void AmGeneric<T>() { } }
中的classNotGeneric
没有任何泛型类型参数,但是它的方法AmGeneric()
有一个 泛型方法类型参数 。假设 AmGeneric()
永远不会 called/used/invoked(“通用实例化”)该方法没有任何 通用方法类型参数 .
鉴于这些 C# classes...
class NormalClass { }
class Generic<T> { }
class Derived : Generic<String> { }
class HasGenericMethod { public void Foo<T>() {} }
...这些 Type
个实例来自 GetGenericArguments()
:
Type[] genericTypeArgs = typeof(Generic<>).GetGenericArguments();
Type genTypeArg = genericTypeArgs.Single();
Type[] genericMethodTypeArgs = typeof(HasGenericMethod).GetMethod( nameof(HasGenericMethod.Foo) ).GetGenericArguments();
Type genMethodArg = genericMethodTypeArgs.Single();
...那么它们的 typeof()
表达式将具有这些属性:
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
您可以自己生成此 table(albiet,转置)using this LinqPad script。
提醒自己:如果您有一个来自 Object.GetType()
的 Type
对象,该对象可能是封闭的泛型类型(即 Object.GetType().IsConstructedGenericType == true
),或者是派生自该泛型的非泛型类型,并且您想找出什么,请执行以下操作:
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;
}
因此下面的代码将打印“成功:T := System.String”两次:
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);
}
脚注:
InvalidOperationException
:“只能在 Type.IsGenericParameter
为真的类型上调用方法。”
InvalidOperationException
: "此操作仅对泛型类型有效。"
typeof(T)
是 typeof(Generic<>).GetGenericArguments().Single()
令人惊讶的是 typeof(T).ContainsGenericParameters == true
当 T
是一个 泛型参数 没有参数集(即 T
是 undefined),所以我本以为会抛出一个 InvalidOperationException
。
- The documentation for
ContainsGenericParameters
似乎有理由返回 true
(强调我的):
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 the ContainsGenericParameters
property returns true
, the type cannot be instantiated.
显然在 T
是构造泛型类型时使用 typeof(T[])
:ContainsGenericParameters
属性 是 false 但 GetGenericArguments()
方法 returns 是 T
的类型参数的非空数组,而不是 System.Array
的类型参数(实际上不是通用类型)。
- 例如:
typeof(Generic<String>[]).IsGenericType == false
typeof(Generic<String>[]).GetGenericArguments() == new[] { typeof(String) }
- 这在上面最右边的一栏中有记载table。
- 并在文档中描述:
The ContainsGenericParameters
property searches recursively for type parameters. For example, it returns true
for an array whose elements are type A<T>
even though the array is not itself generic. Contrast this with the behavior of the IsGenericType
property, which returns false
for arrays.
Type.IsGenericType
和 Type.IsGenericTypeDefinition
和有什么不一样?有趣的是,MSDN 的 IsGenericTypeDefinition link 被破坏了。
尝试检索在给定 DbContext 中定义的所有 DbSet 后,我得到了以下结果,我试图理解这种行为:通过 IsGenericType 过滤属性 returns 期望的结果,而 IsGenericTypeDefinition 不是(return 没有)。
有趣的是,从 this post 我的印象是作者确实使用 IsGenericTypeDefinition 获得了他的 DbSet,而我没有。
下面是说明讨论的示例:
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; }
}
IsGenericType
告诉您 System.Type
的这个实例代表了一个指定了所有类型参数的泛型类型。例如,List<int>
是泛型。
IsGenericTypeDefinition
告诉您 System.Type
的这个实例表示一个定义,可以通过为其类型参数提供类型实参从中构造泛型类型。例如,List<>
是泛型类型定义。
您可以通过调用 GetGenericTypeDefinition
:
var listInt = typeof(List<int>);
var typeDef = listInt.GetGenericTypeDefinition(); // gives typeof(List<>)
您可以通过向 MakeGenericType
:
var listDef = typeof(List<>);
var listStr = listDef.MakeGenericType(typeof(string));
(此答案在下方并排 table 中比较了 Type
的所有泛型类型相关属性,因此如果您已经熟悉 .NET 的泛型并且只需要参考然后向下滚动到 table)
首先,记住参数和参数的区别,尤其是w.r.t。 通用类型参数和通用类型参数(以及通用方法类型参数和泛型方法类型参数):
- 通用类型参数是“开放”通用类型中声明的类型“占位符”。
- 例如,在
class Generic<T0,T1> {}
中,T0
和T1
符号是通用类型参数。请注意,当简单地给出一个未使用的通用class
定义时,则没有 类型参数 .
- 例如,在
- 一个泛型参数是泛型[=256=的消费者为泛型参数指定的类型标识符].
- 例如,在
Generic<String,Object> gen = new Generic<String,Object>
那么...- ...泛型类型参数 for 泛型类型参数
T0
是String
. - ...泛型类型参数 for 泛型类型参数
T1
是Object
.
- ...泛型类型参数 for 泛型类型参数
- 但是,通用类型参数不需要是具体类型:它们可以是来自使用者上下文的通用类型参数。
- 例如,在
class Generic<TItem> { public Object Foo() { return new List<T>(); } }
- ...然后
Generic<TItem>
中的泛型参数TItem
被用作泛型参数T
inList<T>
在Foo
方法内。
- ...然后
- 例如,在
- 例如,在
- 是的,如果您对所有这些感到困惑,请不要担心,因为这很正常。
- 最后,泛型方法类型参数和泛型方法类型参数与泛型类型参数 和 泛型类型参数 除了它们被限制在一个方法中:
- 例如
class NotGeneric { void AmGeneric<T>() { } }
中的classNotGeneric
没有任何泛型类型参数,但是它的方法AmGeneric()
有一个 泛型方法类型参数 。假设AmGeneric()
永远不会 called/used/invoked(“通用实例化”)该方法没有任何 通用方法类型参数 .
- 例如
鉴于这些 C# classes...
class NormalClass { }
class Generic<T> { }
class Derived : Generic<String> { }
class HasGenericMethod { public void Foo<T>() {} }
...这些 Type
个实例来自 GetGenericArguments()
:
Type[] genericTypeArgs = typeof(Generic<>).GetGenericArguments();
Type genTypeArg = genericTypeArgs.Single();
Type[] genericMethodTypeArgs = typeof(HasGenericMethod).GetMethod( nameof(HasGenericMethod.Foo) ).GetGenericArguments();
Type genMethodArg = genericMethodTypeArgs.Single();
...那么它们的 typeof()
表达式将具有这些属性:
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 |
您可以自己生成此 table(albiet,转置)using this LinqPad script。
提醒自己:如果您有一个来自 Object.GetType()
的 Type
对象,该对象可能是封闭的泛型类型(即 Object.GetType().IsConstructedGenericType == true
),或者是派生自该泛型的非泛型类型,并且您想找出什么,请执行以下操作:
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;
}
因此下面的代码将打印“成功:T := System.String”两次:
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);
}
脚注:
InvalidOperationException
:“只能在Type.IsGenericParameter
为真的类型上调用方法。”InvalidOperationException
: "此操作仅对泛型类型有效。"typeof(T)
是typeof(Generic<>).GetGenericArguments().Single()
令人惊讶的是
typeof(T).ContainsGenericParameters == true
当T
是一个 泛型参数 没有参数集(即T
是 undefined),所以我本以为会抛出一个InvalidOperationException
。- The documentation for
ContainsGenericParameters
似乎有理由返回true
(强调我的):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.
- The documentation for
显然在
T
是构造泛型类型时使用typeof(T[])
:ContainsGenericParameters
属性 是 false 但GetGenericArguments()
方法 returns 是T
的类型参数的非空数组,而不是System.Array
的类型参数(实际上不是通用类型)。- 例如:
typeof(Generic<String>[]).IsGenericType == false typeof(Generic<String>[]).GetGenericArguments() == new[] { typeof(String) }
- 这在上面最右边的一栏中有记载table。
- 并在文档中描述:
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.
- 例如: