C# 如何检查类型是否永远不是有效的泛型参数?

C# 如何检查类型是否永远不是有效的泛型参数?,c#,.net,clr,C#,.net,Clr,到目前为止,我的代码如下所示,我想要解决的是摆脱try-catch: public static bool IsNeverValidGenericArgument(this Type type) { var elementType=type.GetElementType(); if(null!=elementType) { if(type.IsArray) try { typeof(IList<>

到目前为止,我的代码如下所示,我想要解决的是摆脱try-catch:

public static bool IsNeverValidGenericArgument(this Type type) {
    var elementType=type.GetElementType();

    if(null!=elementType) {
        if(type.IsArray)
            try {
                typeof(IList<>).MakeGenericType(elementType);
                return false;
            }
            catch(ArgumentException) {
            }
            catch(TypeLoadException) {
            }

        return true; // pointer or byref 
    }

    return
        typeof(void)==type||typeof(RuntimeArgumentHandle)==type
        ||
        typeof(ArgIterator)==type||typeof(TypedReference)==type;
}
但这会将某些类型报告为无效,而实际上不会导致运行时类型中的异常,例如
typeof(ArgIterator).MakeArrayType(2).MakeArrayType()


我知道有些类型不是正常使用的,但我无法避免在消费者代码中使用它们

问题是否是由于您的客户可能传入的类型没有有效的公共无参数构造函数?如果是这种情况,您可以通过向泛型方法添加条件来限制允许他们向您发送的输入:

public class MyClass<T>
    where T : new()
{

}
公共类MyClass
其中T:new()
{
}
此代码只允许具有不带参数的公共构造函数的泛型类型T。你可以在网站上找到更多信息

我不知道您是如何实现变量类型参数的,但是您可以在添加了上述子句的情况下接收类型为t的params集合,如下所示:

public class MyClass<T>
    where T : new()
{
    public void MyMethod(params T[] items)
    {
        //...Do stuff...
    }
}
公共类MyClass
其中T:new()
{
公共作废MyMethod(参数T[]项)
{
//…做事。。。
}
}

这将允许它们传入任意数量的项,但将它们限制为您想要支持的泛型类型。

当您尝试使用参数
typeof(ArgIterator)构造泛型类型时。MakeArrayType().MakeArrayType()
,引发异常的是内部本机CLR代码。这一事实最重要的一点是,抛出的是CLR实现细节,而不是决定泛型类型参数有效性的标准或公开API的一部分。这意味着在不实际尝试的情况下,无法确定泛型类型是否可以构造。编辑:这也意味着没有好的方法来确定某个东西是否可以在特定版本的CLR上工作,而不能在另一个版本上工作

但是,更重要的是,如果您试图用无效参数构造泛型类型,这确实是一个例外情况,正确的做法是抛出一个异常。我不知道你的代码是做什么的,但是如果你担心你的消费者调用它时会抛出
TypeLoadException
s类,也许你应该让这些错误冒出来,让消费者知道有问题


TL;DR:您可能不应该做任何您试图做的事情来处理异常情况。让它抛出。

如果您添加一些测试用例,以便我们的编码人员能够明确了解您的期望,这将是非常有用的。但是你提供了一笔赏金,所以我要看看这是否接近你想要的

下面代码的结果:

测试具有定义测试
实际类型为UserQuery+Test`1[System.Int32]

公共静态类扩展
{
/// 
///检查此类型在其祖先中是否具有指定的定义。
///    
公共静态bool HasGenericDefinition(此类型,类型定义)
{
返回GetTypeWithGenericDefinition(类型,定义)!=null;
}
/// 
///从中返回实现指定定义的实际类型
///类型的祖先(如果可用)。否则,为null。
/// 
公共静态类型GetTypeWithGenericDefinition(此类型,类型定义)
{
如果(type==null)抛出新的ArgumentNullException(“type”);
如果(definition==null)抛出新的ArgumentNullException(“definition”);
如果(!definition.IsGenericTypeDefinition)抛出新的ArgumentException(“定义需要是GenericTypeDefinition”、“定义”);
if(定义.i接口)
foreach(type.GetInterfaces()中的var interfaceType)
if(interfaceType.IsGenericType&&interfaceType.GetGenericTypeDefinition()==定义)
返回接口类型;
for(类型t=Type;t!=null;t=t.BaseType)
if(t.IsGenericType&&t.GetGenericTypeDefinition()==定义)
返回t;
返回null;
}
}
void Main()
{
a型=型式(试验);
b型=型式(试验);
if(a.HasGenericDefinition(b))Console.WriteLine(“测试有定义测试”);
类型c=a.GetTypeWithGenericDefinition(b);
WriteLine(“实际类型为{0}”,c.ToString());
}
公开课考试
{
公开考试()
{
}
}

我看不出它能比你已经做的更好。以下是我的版本:

// Predicts whether the given type cannot be used as a type argument.
public static bool IsNeverValidGenericArgument(this Type type)
{
  if (type == null)
    throw new ArgumentNullException("type");

  // Pointer types and ByRef types.
  if (type.IsPointer || type.IsByRef)
    return true;

  // The following four special cases were found by reflecting through all types in mscorlib.dll, System.dll, and System.Core.dll.
  // The list may be different in other versions of the framework.
  var exceptions = new HashSet<Type>
  {
    typeof(ArgIterator), typeof(RuntimeArgumentHandle), typeof(TypedReference), typeof(void),
  };
  return exceptions.Contains(type);
}
其中,
System.Math
是一个静态类(抽象和密封类型)。对于这样的静态类,我的方法仍然会返回
false

有些类型甚至不存在,例如
type=typeof(int).MakeByRefType().MakeArrayType()
(将抛出),因此我的方法无法检查这些混蛋。

以下是对使用ArgIterator解决此问题的代码“替代”版本的更新

    public static bool IsNeverValidGenericArgument(this Type type)
    {
        return type.IsNeverValidGenericArgument(true);
    }

    private static bool IsNeverValidGenericArgument(this Type type, bool isRoot)
    {
        var elementType = type.GetElementType();

        if (null != elementType)
        {
            if (type.IsArray)
                return elementType.IsNeverValidGenericArgument(false);

            return true; // pointer or byref 
        }

        if (isRoot)
        {
            return
                typeof(void) == type || typeof(RuntimeArgumentHandle) == type
                ||
                typeof(ArgIterator) == type || typeof(TypedReference) == type;
        }
        else
        {
            return (typeof(void) == type || typeof(TypedReference) == type);
        }
    }

你为什么真的要检查这个?如果你想使用泛型,编译器会执行这种检查吗?@DanielHilgarth:因为没有语法规定使用者代码可以指定可变类型参数,所以我只能使用可变方法来接收类型参数,就像
MakeGenericType
所做的那样。这并不能回答我的问题。如果您在C程序中编写以下代码,您会遇到编译器错误吗<代码>新列表()如果没有,为什么需要处理这种特殊情况?换句话说,您计划如何使用
isnevervalidenericargument
?@DanielHilgarth:whatevertypethrouwsanexception是编译器不允许的。我正在动态构造类型,只要使用
makexxtype
生成并且CLR允许,我就不应该排除它们。我在
GetInt的包装版本中调用
isnervalidgenericargument
// Predicts whether the given type cannot be used as a type argument.
public static bool IsNeverValidGenericArgument(this Type type)
{
  if (type == null)
    throw new ArgumentNullException("type");

  // Pointer types and ByRef types.
  if (type.IsPointer || type.IsByRef)
    return true;

  // The following four special cases were found by reflecting through all types in mscorlib.dll, System.dll, and System.Core.dll.
  // The list may be different in other versions of the framework.
  var exceptions = new HashSet<Type>
  {
    typeof(ArgIterator), typeof(RuntimeArgumentHandle), typeof(TypedReference), typeof(void),
  };
  return exceptions.Contains(type);
}
typeof(IList<>).MakeGenericType(typeof(Math))  // will work
// but C# does not allow the notation IList<Math>
    public static bool IsNeverValidGenericArgument(this Type type)
    {
        return type.IsNeverValidGenericArgument(true);
    }

    private static bool IsNeverValidGenericArgument(this Type type, bool isRoot)
    {
        var elementType = type.GetElementType();

        if (null != elementType)
        {
            if (type.IsArray)
                return elementType.IsNeverValidGenericArgument(false);

            return true; // pointer or byref 
        }

        if (isRoot)
        {
            return
                typeof(void) == type || typeof(RuntimeArgumentHandle) == type
                ||
                typeof(ArgIterator) == type || typeof(TypedReference) == type;
        }
        else
        {
            return (typeof(void) == type || typeof(TypedReference) == type);
        }
    }