C# 将类型参数传递给无约束的泛型类型会引发约束异常

C# 将类型参数传递给无约束的泛型类型会引发约束异常,c#,.net,generics,types,C#,.net,Generics,Types,考虑以下代码: IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object)) .GetTypes() .Where(t => t.IsGenericType) .Wh

考虑以下代码:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object))
                                      .GetTypes()
                                      .Where(t => t.IsGenericType)
                                      .Where(t => t.GetGenericArguments().Length == 1)
                                      .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0);
这应该管用,对吧?我的意思是,
oneParameterTypes
中的所有类型都应该没有类型约束,因此
System.Int32
应该是有效的类型

但是,该行抛出以下异常:

“System.RuntimeType+ListBuilder`1[T]”上的GenericArguments[0]、“System.Int32”违反了类型“T”的约束


这是什么
ListBuilder'1
类型?如果它有类型约束,为什么它在
oneParameterTypes
中?为什么我的
Where
过滤器不工作?

我认为克里斯是对的

试着这样做:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof (object))
    .GetTypes()
    .Where(t => t.IsGenericType)
    .Where(t => t.GetGenericArguments().Length == 1)
    .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 &&
                !genericArgument.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);
    });
IEnumerable oneParameterTypes=Assembly.GetAssembly(typeof(object))
.GetTypes()
.Where(t=>t.IsGenericType)
.Where(t=>t.GetGenericArguments().Length==1)
.Where(t=>t.GetGenericArguments().Single().GetGenericParameterConstraints().Length==0&&
!genericArgument.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);
});
typeof()或.GetType()方法不返回实际的类,而是返回类型对象表示,这将帮助我们将类型名称用作字符串


相反,您可以使用反射类并找到该类型的类并将其用作参数。文档并不清楚
类型。GetGenericParameterConstraints
实际上查找的是:

使用IsClass属性确定约束是否为基类约束;如果属性返回false,则约束是接口约束如果类型参数没有类约束和接口约束,则返回空数组。

您可以隐式地理解,它只检查基类或接口约束。要全面了解存在哪些约束,您需要对
GenericParameterAttributes
进行额外检查。您可以使用
HasFlag
或位掩码:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object))
              .GetTypes()
              .Where(t => t.IsGenericType)
              .Where(t => t.GetGenericArguments().Length == 1)
              .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 && !t.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);
IEnumerable oneParameterTypes=Assembly.GetAssembly(typeof(object))
.GetTypes()
.Where(t=>t.IsGenericType)
.Where(t=>t.GetGenericArguments().Length==1)
.Where(t=>t.GetGenericArguments().Single().GetGenericParameterConstraints().Length==0&&!t.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);

我认为这是因为类对
class
的泛型类型有约束,但对特定类没有约束。(而不是
struct
),例如,如果在空的
公共类测试上运行代码,其中T:class{}
,它将以完全相同的方式失败。在我的脑海中,我不知道你到底是如何检查它的,或者为什么它不一定包含在你的
Where
子句中。@ChrisSinclair这太令人失望了。
其中t:class
是一个类型约束,但它没有包含在
type.GetGenericParameterConstraints()中
。这是故意的还是一个bug?我不这么认为。请看。这只是您必须查找的附加属性。我相信
GetGenericParameterConstraints
只检查类型继承约束,而不是
class/struct
或协方差/逆变约束。编辑:这些可能被视为“属性”而不是约束。无论如何,我同意命名有点模糊或混乱,即使它在技术上是正确的(假设是)。我认为这可能会起作用,尽管您可能希望使用
!=GenericParameterAttributes进行检查。请改为引用TypeConstraint
(因为您可能还需要包括一些其他内容,如
in
out
)正确。我最初的尝试返回了125项,其中包含
!=GenericParameterAttributes的项。ReferenceTypeConstraint
返回141项。此外,我仍然不正确。我没有注意到
GenericParameterAttributes
是位标志枚举,这在回顾中是有意义的。因此,您不应该检查
!=GenericParameterAttributes。ReferenceTypeConstraint
也可以,但要进行位标志检查,看看它是否包含该标志。正确,除了一个小的吹毛求疵之外,它被称为HasFlag,而不是HasFlags。@Szabolcs-Thx,已修复。
IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object))
              .GetTypes()
              .Where(t => t.IsGenericType)
              .Where(t => t.GetGenericArguments().Length == 1)
              .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 && !t.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);