如何在.NET中的“容器类型”中获得“基本”类型?

时间:2010-01-30 18:53:48

标签: c# .net types

我正在使用一种方法,当传递List<List<int>>时将返回{int},传递时double[,]将返回{double},传递Dictionary<string, byte[]>时将返回{string, byte}等等。基本上,它是关于递归输入类型,直到找到的类型不再是“容器类型”,然后报告这种“基本”类型。

我最初的猜测是测试IEnumerable实现的输入类型,但这似乎不起作用。我也对GetNestedTypes()做了一些试验和错误,但这似乎是无关的。我最终得到了以下方法,它取决于ICollection。我扔了一些非常奇怪的类型,它似乎工作;我想知道的是,该方法是否覆盖了那里的所有容器类型,或者它是否遗漏了某些东西(即它实际上是否有效或者我的测试结果是“快乐的巧合”?)

非常感谢。

编辑(1):如果通过,比如Dictionary<Foo, Bar>,方法返回{Foo, Bar}是可以的,无论这些类型的内部结构如何(它不需要超出)。

    public void GetPrimitiveTypes(Type inputType, ref List<Type> primitiveTypes)
    {
        if (inputType.IsGenericType)
            foreach (Type genericArg in inputType.GetGenericArguments())
                GetPrimitiveTypes(genericArg, ref primitiveTypes);

        if (inputType.IsArray)            
            GetPrimitiveTypes(inputType.GetElementType(), ref primitiveTypes);

        if (inputType.GetInterface("ICollection") == null)
            primitiveTypes.Add(inputType);
    }

编辑(2):这是一个改进的版本(我相信)。它执行一些错误处理并用IsArray替换HasElementType(来自Josh的回答)。我也决定留下指针。除了空和指针,假设是:(1)所有“容器”类型实现ICollection和(2)实现ICollection的所有类型都是通用的或具有元素类型。如果这些假设是正确的,那么我认为下面的方法应该有效,但这是我不确定的。我在问题中也用“基本”取代了“原始”,因为“原始”有其自己的不同含义。

    public void GetPrimitiveTypes2(Type inputType, ref List<Type> primitiveTypes)
    {
        // Handle null
        if (inputType == null)
            throw new ArgumentNullException("inputType");

        // Leave pointers out
        if (inputType.IsPointer)
            throw new ArgumentException(message: "Pointer types are not supported", paramName: "inputType");

        // Option 1: type is generic
        if (inputType.IsGenericType)
        {
            foreach (Type genericArg in inputType.GetGenericArguments())
                GetPrimitiveTypes2(genericArg, ref primitiveTypes);

            return;
        }

        // Option 2: type has element type
        if (inputType.HasElementType)
        {
            GetPrimitiveTypes2(inputType.GetElementType(), ref primitiveTypes);
            return;
        }

        // Option 3: type is not a container
        // Remark: can't use IsPrimitive as it will be false for, say, type Foo
        if (inputType.GetInterface("ICollection") == null)
        {
            primitiveTypes.Add(inputType);
            return;
        }

        // Do options 1-3 above cover all cases?
        throw new ArgumentException(message: "Unhandled type", paramName: "inputType");
    }

2 个答案:

答案 0 :(得分:1)

这是我使用的方法。你会发现它和你的没什么不同。但它不处理具有多个类型参数的泛型。

    /// <summary>
    /// When given certain types such as those based on <see cref="T:Nullable`1"/>, <see cref="T:IEnumerable`1"/>,
    /// or <see cref="T:Array"/>, returns the element type associated with the input type.
    /// </summary>
    /// <remarks>
    /// For example, calling this method with Nullable(Of Boolean) would return Boolean. Passing Int32[] would
    /// return Int32, etc. All other types will return the input.
    /// </remarks>
    /// <param name="type">The a nullable type, array type, etc. whose element type you want to retrieve.</param>
    /// <returns>The type that the input type is based on.</returns>
    public static Type GetElementType( Type type )
    {

        ParameterValidation.ThrowIfNull( type, "type" );

        if ( type.IsGenericType ) {
            var typeArgs = type.GetGenericArguments( );
            if ( typeArgs.Length == 1 ) {
                return typeArgs[0];
            }   // if
        }   // if

        if ( type.HasElementType ) {
            return type.GetElementType( );
        }   // if

        if ( type.IsEnum ) {
            return Enum.GetUnderlyingType( type );
        }   // if

        return type;

    }

答案 1 :(得分:0)

以Josh的答案为基础,但更正术语并将结果限制为集合/枚举:

public static Type GetElementType(Type type)
{
    if (type == null)
        throw new ArgumentNullException("type");

    if (type.HasElementType)
        return type.GetElementType();

    Type[] interfaces = type.GetInterfaces();
    foreach (Type t in interfaces)
    {
        if (t.IsGenericType)
        {
            Type generic = t.GetGenericTypeDefinition();
            if (generic == typeof(IEnumerable<>))
                return t.GetGenericArguments()[0];
        }
    }

    /* If you want to allow weakly typed collections (and just have element type
     * Object), you can uncomment the following:
     */
    //if (typeof(IEnumerable).IsAssignableFrom(type))
    //    return typeof(object);

    return null;
}

public static Type GetUnderlyingType(Type type)
{
    if (type == null)
        throw new ArgumentNullException("type");

    if (type.IsEnum)
        return type.GetEnumUnderlyingType();

    return Nullable.GetUnderlyingType(type);
}