将实例化的 System.Type 作为泛型类的类型参数传递
Posted
技术标签:
【中文标题】将实例化的 System.Type 作为泛型类的类型参数传递【英文标题】:Pass An Instantiated System.Type as a Type Parameter for a Generic Class 【发布时间】: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<T>(T obj)
- 自那条评论以来的过去 6 年里,我已经多次使用过这个技巧;p
@MarcGravell 有没有办法修改它,以便方法实例化它?
【参考方案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不幸的是,没有。泛型参数必须在编译时可解析为 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<FooContent> contents)
new Encoder().Markdown( (dynamic) dynamicList)
注意方法调用中dynamic
的用法。在运行时dynamicList
将是List<FooContent>
(另外也是IEnumerable<FooContent>
),因为即使使用动态仍然植根于强类型语言,运行时绑定器将选择适当的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);
同样,您可以调用列表中的任何其他方法。
【讨论】:
以上是关于将实例化的 System.Type 作为泛型类的类型参数传递的主要内容,如果未能解决你的问题,请参考以下文章