C#Reflection - 用于递归类型参数的MakeGenericType

时间:2017-09-13 17:04:56

标签: c# generics reflection

在我项目的某个地方,我需要创建一个具体的泛型类型,将泛型类型定义(带有一个参数)和一个参数类型作为参数。

为此,我编写了一个非常简单的方法:

to_f

但是,在某些时候,我需要创建一个类型,比如Type MakeGenericType(Type definition, Type parameter) { return definition.MakeGenericType(parameter); } ,其中给定的元素类型为List<List<T>>。虽然我可以使用my方法创建类型T,但后续尝试从中创建具体类型List<List<T>>失败 - 请参阅下面的代码:

List<List<int>>
  

未处理的类型&#39; System.InvalidOperationException&#39;   发生在mscorlib.dll

     

其他信息:   System.Collections.Generic.List`1 [System.Collections.Generic.List`1 [T]]   不是GenericTypeDefinition。 MakeGenericType只能被调用   Type.IsGenericTypeDefinition为true的类型。

此外,以下电话甚至无法编译:

var genericList = MakeGenericType(typeof(List<>), typeof(List<>)); // success

MakeGenericType(genericList, typeof(int)); // exception

我已经MakeGenericType(typeof(List<List<>>), typeof(int)); 检查了IsGenericTypeDefinitionContainsGenericParameters之间的区别。但是,我仍然没有想法,如何处理像genericList这样的类型对象。

显然,使用反射我可以构造一个类型对象,这与它无关 - 这对我来说非常混乱。

所以问题是,如何从泛型创建具体类型,其中包含泛型类型定义作为参数?它有可能吗?

3 个答案:

答案 0 :(得分:4)

您需要将传递的类型分解为泛型类型定义,并使用以下内容构建生成的泛型类型:

static Type MakeGenericType(Type definition, Type parameter)
{
    var definitionStack = new Stack<Type>();
    var type = definition;
    while (!type.IsGenericTypeDefinition)
    {
        definitionStack.Push(type.GetGenericTypeDefinition());
        type = type.GetGenericArguments()[0];
    }
    type = type.MakeGenericType(parameter);
    while (definitionStack.Count > 0)
        type = definitionStack.Pop().MakeGenericType(type);
    return type;
}

答案 1 :(得分:1)

type参数本身就是一个泛型类型,所以只需要编写匹配的函数:

// List<List<int>>
MakeGenericType(
    typeof(List<>),
    MakeGenericType(
        typeof(List<>),
        typeof(int)
    )
);

答案 2 :(得分:1)

Ivan Stoev的答案外,这里还有MakeGenericType的{​​{1}}版本,可以生成和处理部分定义的&#34;泛型类型:

Type MakeGenericType(Type type, Type parameter)
{
    if (!type.ContainsGenericParameters)
        throw new ArgumentException(nameof(type));

    bool replaced = false;

    return MakeGenericType(type, parameter, ref replaced);
}

Type MakeGenericType(Type type, Type parameter, ref bool replaced)
{
    if (type.IsGenericParameter)
        if (replaced)
            return type;
        else
        {
            replaced = true;
            return parameter;
        }

    if (type.IsGenericTypeDefinition)
    {
        var parameters = type.GetTypeInfo().GenericTypeParameters.ToArray();

        parameters[0] = parameter;
        replaced = true;

        return type.MakeGenericType(parameters);
    }

    if (type.IsGenericType && type.ContainsGenericParameters)
    {
        var parameters = type.GenericTypeArguments.ToArray();

        for (int i = 0; i < parameters.Length; i++)
            parameters[i] = MakeGenericType(parameters[i], parameter, ref replaced);

        return type
            .GetGenericTypeDefinition()
            .MakeGenericType(parameters);
    }

    return type;
}

以下来电成功:

var d0 = MakeGenericType(typeof(Dictionary<,>), typeof(Tuple<,>)); // Dictionary`2[Tuple`2[T1,T2],TValue]
var d1 = MakeGenericType(d0, typeof(double));                      // Dictionary`2[Tuple`2[Double,T2],TValue]
var d2 = MakeGenericType(d1, typeof(float));                       // Dictionary`2[Tuple`2[Double,Single],TValue]
var d3 = MakeGenericType(d2, typeof(Func<,,>));                    // Dictionary`2[Tuple`2[Double,Single],Func`3[T1,T2,TResult]]
var d4 = MakeGenericType(d3, typeof(short));                       // Dictionary`2[Tuple`2[Double,Single],Func`3[Int16,T2,TResult]]
var d5 = MakeGenericType(d4, typeof(object));                      // Dictionary`2[Tuple`2[Double,Single],Func`3[Int16,Object,TResult]]
var d6 = MakeGenericType(d5, typeof(int));                         // Dictionary`2[Tuple`2[Double,Single],Func`3[Int16,Object,Int32]]