传递实例化的System.Type作为泛型类的类型参数
本文关键字:泛型类 类型参数 System 实例化 Type | 更新日期: 2023-09-27 17:48:48
标题有点晦涩。我想知道的是这是否可能:
string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);
MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();
显然,MyGenericClass被描述为:
public class MyGenericClass<T>
现在,编译器抱怨找不到"类型或命名空间"myType"。"必须有办法做到这一点。
没有反射就无法做到这一点。但是,可以通过反射来完成。这里有一个完整的例子:
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);
遗憾的是没有。泛型参数在编译时必须可解析为1)有效类型或2)另一个泛型参数。如果不使用反射,就无法基于运行时值创建通用实例。
使用剪刀式代码运行的一些附加方法。假设你有一个类似的类
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
方法。如果没有完全匹配的类型,它将查找对象参数方法,如果两者都不匹配,则将引发运行时绑定器异常,警告没有匹配的方法。
这种方法的明显缺点是编译时类型安全性的巨大损失。尽管如此,沿着这些行的代码将允许您在一个非常动态的意义上操作,即在运行时仍然是完全类型化的。
我的要求略有不同,但希望能帮助到一些人。我需要从配置中读取类型并动态实例化泛型类型。
namespace GenericTest
{
public class Item
{
}
}
namespace GenericTest
{
public class GenericClass<T>
{
}
}
最后,这里是你如何称呼它。用一个反勾来定义类型。
var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);
如果您知道将传递哪些类型,则可以在不进行反射的情况下执行此操作。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;
}
}
}
在这个片段中,我想展示如何创建和使用动态创建的列表。例如,我在这里添加到动态列表中。
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);
类似地,您可以调用列表中的任何其他方法。