【问题标题】:Pass An Instantiated System.Type as a Type Parameter for a Generic Class将实例化的 System.Type 作为泛型类的类型参数传递
【发布时间】:2010-09-20 22:26:34
【问题描述】:

标题有点晦涩。我想知道的是这是否可能:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

显然,MyGenericClass 被描述为:

public class MyGenericClass<T>

现在,编译器抱怨“找不到类型或命名空间 'myType'。”必须有办法做到这一点。

【问题讨论】:

  • 泛型 != 模板。所有泛型类型变量都在编译时解析,而不是在运行时解析。这是 4.0 的“动态”类型可能有用的情况之一。
  • @Will - 以什么方式?当与泛型一起使用时,在当前的 CTP 下,您基本上最终会调用 版本(除非我错过了一个技巧......)
  • @MarcGravell 您可以使用foo.Method((dynamic)myGenericClass) 进行运行时方法绑定,有效地为类型的方法重载提供服务定位器模式。
  • @ChrisMarisic 是的,对于一些通用的public void Method&lt;T&gt;(T obj) - 自那条评论以来的过去 6 年里,我已经多次使用过这个技巧;p
  • @MarcGravell 有没有办法修改它,以便方法实例化它?

标签: c# .net generics


【解决方案1】:

没有反思就无法做到这一点。但是,您可以通过反射来做到这一点。这是一个完整的例子:

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

注意:如果您的泛型类接受多种类型,则在省略类型名称时必须包含逗号,例如:

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);

【讨论】:

  • 好的,这很好,但是如何在创建时调用方法呢?更多反思?
  • 好吧,如果你可以让你的泛型类型实现一个非泛型接口,你可以转换到那个接口。或者,您可以编写自己的泛型方法来完成您想要对泛型执行的所有工作,然后使用反射调用 that。
  • 是的,如果您拥有的关于返回类型的唯一信息是在 typeArgument 类型变量中,我不明白如何使用 created?在我看来,您必须强制转换为该变量,但您不知道它是什么,所以我不确定您是否可以通过反射来做到这一点。另一个问题,如果对象是 int 类型,如果你将它作为对象变量传递给例如 List 这样的工作吗?创建的变量会被视为 int 吗?
  • @RobertC.Barth 您还可以将示例中的“已创建”对象类型设为“动态”而不是“对象”。这样你就可以调用它的方法,并且评估将被推迟到运行时。
  • @balanza:你使用 MakeGenericMethod。
【解决方案2】:

不幸的是,没有。泛型参数必须在编译时可解析为 1) 有效类型或 2) 另一个泛型参数。如果没有使用反射的大锤,就无法根据运行时值创建通用实例。

【讨论】:

    【解决方案3】:

    一些额外的如何使用剪刀代码运行。假设你有一个类似于

    的类
    public class Encoder() {
    public void Markdown(IEnumerable<FooContent> contents) { do magic }
    public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
    }
    

    假设在运行时你有一个 FooContent

    如果你能够在编译时绑定你会想要的

    var fooContents = new List<FooContent>(fooContent)
    new Encoder().Markdown(fooContents)
    

    但是您不能在运行时执行此操作。要在运行时执行此操作,您可以按照以下方式进行:

    var listType = typeof(List<>).MakeGenericType(myType);
    var dynamicList = Activator.CreateInstance(listType);
    ((IList)dynamicList).Add(fooContent);
    

    动态调用Markdown(IEnumerable&lt;FooContent&gt; contents)

    new Encoder().Markdown( (dynamic) dynamicList)
    

    注意方法调用中dynamic 的用法。在运行时dynamicList 将是List&lt;FooContent&gt;(另外也是IEnumerable&lt;FooContent&gt;),因为即使使用动态仍然植根于强类型语言,运行时绑定器将选择适当的Markdown 方法。如果没有精确的类型匹配,它将寻找对象参数方法,如果两者都不匹配,则会引发运行时绑定器异常,提醒没有方法匹配。

    这种方法的明显缺点是编译时类型安全性的巨大损失。尽管如此,这些代码将让您以一种非常动态的方式进行操作,在运行时仍然是完全类型化的,就像您期望的那样。

    【讨论】:

      【解决方案4】:

      我的要求略有不同,但希望能对某人有所帮助。我需要从配置中读取类型并动态实例化泛型类型。

      namespace GenericTest
      {
          public class Item
          {
          }
      }
      
      namespace GenericTest
      {
          public class GenericClass<T>
          {
          }
      }
      

      最后,这是你如何称呼它的。 Define the type with a backtick.

      var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
      var a = Activator.CreateInstance(t);
      

      【讨论】:

        【解决方案5】:

        如果您知道将传递哪些类型,则无需反射即可执行此操作。 switch 语句将起作用。显然,这只会在有限的情况下起作用,但它会比反射快得多。

        public class Type1 { }
        
        public class Type2 { }
        
        public class Generic<T> { }
        
        public class Program
        {
            public static void Main()
            {
                var typeName = nameof(Type1);
        
                switch (typeName)
                {
                    case nameof(Type1):
                        var type1 = new Generic<Type1>();
                        // do something
                        break;
                    case nameof(Type2):
                        var type2 = new Generic<Type2>();
                        // do something
                        break;
                }
            }
        }
        

        【讨论】:

        • 一旦你开始处理 100 多个类,这会很快变得丑陋。
        【解决方案6】:

        在这个 sn-p 中,我想展示如何创建和使用动态创建的列表。比如我这里添加到动态列表中。

        void AddValue<T>(object targetList, T valueToAdd)
        {
            var addMethod = targetList.GetType().GetMethod("Add");
            addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
        }
        
        var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
        var list = Activator.CreateInstance(listType);
        
        AddValue(list, 5);
        

        同样,您可以调用列表中的任何其他方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-05
          • 2019-02-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多