7

我到目前为止的代码如下,我要解决的是摆脱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;
}

我正在尝试编写动态类型构造的代码,我的代码将GetInterfaces()在传递的每种类型上调用,但是消费者代码传递的某些类型可能会在内部导致一个TypeLoadExceptionin RuntimeType(例如typeof(ArgIterator).MakeArrayType().MakeArrayType()在 3.5 中,但不是 4.0+),我首先需要检查它是否永远不是有效的通用参数。try-catch 有效,但没有用。

请注意,它抛出的情况可能会因 .Net framework 的不同版本而异


编辑:

该方法的另一个版本是:

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

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

        return true; // pointer or byref 
    }

    return
        typeof(void)==type||typeof(RuntimeArgumentHandle)==type
        ||
        typeof(ArgIterator)==type||typeof(TypedReference)==type;
}

但这会将某些类型报告为无效,实际上不会导致异常RuntimeType,例如typeof(ArgIterator).MakeArrayType(2).MakeArrayType().

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

4

5 回答 5

2

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

但是,更重要的是,如果您尝试使用无效参数构造泛型类型,那确实是一种例外情况,正确的做法是抛出异常。我无法谈论您的代码做了什么,但如果您担心您的消费者使用导致TypeLoadExceptions 被抛出的类来调用它,也许您应该让这些错误冒泡,以便消费者知道存在问题。

TL;DR:你可能不应该做任何你想做的事情来处理异常情况。就让它扔吧。

于 2013-08-09T20:17:55.897 回答
1

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

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

}

此代码仅允许具有没有参数的公共构造函数的泛型类型 T。您可以在此处找到有关 where 子句的更多信息

我不知道您是如何实现可变参数类型参数的,但您可以使用 T 类型的 params 集合并添加上述子句,如下所示:

public class MyClass<T>
    where T : new()
{
    public void MyMethod(params T[] items)
    {
        //...Do stuff...
    }
}

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

于 2013-08-09T20:13:35.793 回答
1

我看不出它可以比你已经做的更好这是我的版本:

// 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);
}

请注意,这仅考虑类型系统是否将构造一个封闭的泛型类型type作为其类型参数。有些类型非常无意义,例如:

typeof(IList<>).MakeGenericType(typeof(Math))  // will work
// but C# does not allow the notation IList<Math>

whereSystem.Math是一个静态类(抽象和密封类型)。我的方法仍然会返回false这样的静态类。

有些类型甚至都不存在,比如type = typeof(int).MakeByRefType().MakeArrayType()(will throw),所以这些混蛋用我的方法是查不出来的。

于 2013-08-13T08:45:10.423 回答
1

如果您添加了一些测试用例,以便我们的编码人员可以具体了解您的期望,这将很有帮助。但是你提供了赏金所以我看看这是否接近你想要的。

下面的代码结果:

测试有定义 Test<>
实际类型是 UserQuery+Test`1[System.Int32]

public static class Extensions
{
    /// <summary>
    /// Checks whether this type has the specified definition in its ancestry.
    /// </summary>   
    public static bool HasGenericDefinition(this Type type, Type definition)
    {
        return GetTypeWithGenericDefinition(type, definition) != null;
    }

    /// <summary>
    /// Returns the actual type implementing the specified definition from the
    /// ancestry of the type, if available. Else, null.
    /// </summary>
    public static Type GetTypeWithGenericDefinition(this Type type, Type definition)
    {
        if (type == null) throw new ArgumentNullException("type");
        if (definition == null) throw new ArgumentNullException("definition");
        if (!definition.IsGenericTypeDefinition) throw new ArgumentException("The definition needs to be a GenericTypeDefinition", "definition");

        if (definition.IsInterface)
            foreach (var interfaceType in type.GetInterfaces())
                if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == definition)
                    return interfaceType;

        for (Type t = type; t != null; t = t.BaseType)
            if (t.IsGenericType && t.GetGenericTypeDefinition() == definition)
                return t;

        return null;
    }
}

void Main()
{
    Type a = typeof(Test<int>);
    Type b = typeof(Test<>);
    if(a.HasGenericDefinition(b)) Console.WriteLine("Test<int> has definition Test<>");
    Type c = a.GetTypeWithGenericDefinition(b);
    Console.WriteLine("Actual Type is {0}", c.ToString());
}

public class Test<T>
{
    public Test()
    {
    }
}
于 2013-08-09T21:51:32.973 回答
0

这是解决 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);
        }
    }
于 2013-08-16T14:47:49.683 回答