【问题标题】:C# Reflection - MakeGenericType for recursive type parametersC# Reflection - MakeGenericType 用于递归类型参数
【发布时间】:2017-09-14 15:05:46
【问题描述】:

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

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

Type MakeGenericType(Type definition, Type parameter)
{
    return definition.MakeGenericType(parameter);
}

但是,在某些时候,我需要使用给定的元素类型 T 创建一个类型,例如 List<List<T>>。虽然我可以使用我的方法创建类型 List<List<T>>,但随后尝试从中创建具体类型 List<List<int>> 失败 - 请参见下面的代码:

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

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

“System.InvalidOperationException”类型的未处理异常 发生在 mscorlib.dll 中

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

此外,以下调用甚至不会编译:

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

我检查了this question 关于IsGenericTypeDefinitionContainsGenericParameters 之间的区别。但是,我仍然不知道如何处理像genericList 这样的类型对象。

显然,使用反射我可以构造一个类型对象,这与它无关——这让我很困惑。

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

【问题讨论】:

    标签: c# generics reflection


    【解决方案1】:

    您需要将传递的类型分解为泛型类型定义,并使用以下方法自下而上构建生成的泛型类型:

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

    【讨论】:

      【解决方案2】:

      类型参数本身是一个泛型类型,所以只需组合函数来匹配:

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

      【讨论】:

      • 是的,但我的问题是,我该如何处理List&lt;List&lt;T&gt;&gt; 类型的对象?我可以构建它,但显然我根本无法使用它。这很奇怪。
      • @stop-can, MakeGenericType 期望第一个参数是“开放”泛型(尚未指定类型参数),但typeof(List&lt;List&lt;&gt;&gt;) 不是“开放”;它的类型参数是'open',但它确实有一个类型参数(typeof(List&lt;&gt;))。这是一个有效的类型,但它不适用于MakeGenericType
      • @DanBryant 好的,它在MakeGenericType 中不可用,但我可以以其他方式使用它吗?我对部分类型计算感兴趣——比如柯里化,如果将泛型类型定义视为类型参数的函数。
      • @stop-cran,您应该可以使用Type 的属性/方法查询开放类型及其参数。如果您愿意,您可以创建自己的 MakeGenericType 版本,它递归泛型参数并将其应用于“最内层”泛型。
      【解决方案3】:

      除了Ivan Stoev的回答,这里有一个MakeGenericType的版本,可以产生和处理“部分定义”的泛型类型:

      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]]
      

      【讨论】:

        猜你喜欢
        • 2020-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多