如何从C#中的字符串创建generics类?
我有一个像这样的Generic类:
public class Repository {...}
我需要用字符串来实例…示例:
string _sample = "TypeRepository"; var _rep = new Repository();
我怎样才能做到这一点? 这有可能吗?
谢谢!
这是我的2美分:
Type genericType = typeof(Repository<>); Type[] typeArgs = { Type.GetType("TypeRepository") }; Type repositoryType = genericType.MakeGenericType(typeArgs); object repository = Activator.CreateInstance(repositoryType);
回答评论中的问题。
MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething"); MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something)); closedMethod.Invoke(repository, new[] { "Query String" });
首先使用Type.GetType(stringContainingTheGenericTypeArgument)
获取Type对象Type.GetType(stringContainingTheGenericTypeArgument)
然后使用typeof(Repository<>).MakeGenericType(theTypeObject)
来获取generics类型。
最后使用Activator.CreateInstance
这是C#4.0中dynamic
关键字的工作。
这里有一些不错的黑客可以让你获得你的对象的实例,但无论当前版本的C#只知道它有一个object
,并且它对一个对象本身无法做太多因为当前的C#没有支持后期绑定。 您需要能够至少将其转换为已知类型以对其执行任何操作。 最终,您必须在编译时知道所需的类型,否则您将失去运气。
现在,如果您可以将存储库类限制为实现某个已知接口的类型,那么您的形状会更好。
如果我正确理解你的问题……你要做的是采用你的类型(Repository
如果是这样,请查看MakeGenericType 。 您可以使用typeof(Repository)和T所需对象的System.Type并以这种方式构造它。 一旦你有了类型,Activator.CreateInstance将适合你。
假设您的字符串包含类型的名称,您可以编写
object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample)));
但是,_rep将是一个无类型的对象,你将无法用它做任何事情。 由于您不知道generics类在编译时的类型,因此没有将其强制转换为的类型。 (除非Repositoryinheritance非generics类或实现非generics接口)
您可以通过为Repository创建一个非generics基类并将对象转换为它来解决这个问题。
但是,根据您的具体情况,您可能希望将Repository
设为非generics类。
如果Repository
是包含其他类的类,则最好将其设置为非generics。 您可以使其构造函数采用Type
对象,然后对您添加的每个对象调用Type.IsInstanceOfType
,以确保它是正确的类型。
如果Repository是一个分类的事物集合,那么最好使它成为非generics的,并使其构造函数采用string category
。
如果您需要更具体的建议,请发布有关您情况的更多详细信息。
Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String")); object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName);
这将创建一个Repository
的实例。 您可以将“System.String”替换为您喜欢的任何类型。
generics可以处理类型,而不是实例。 你可以在这里阅读更多。
听起来你只需要在你的类中添加一个构造函数,它接受所需的类型并初始化值:
public class Foo { private T = default(T); public Foo(T initValue) { _val = T; } }
这有点“可行”。 你可以做到这一点,但它有点像黑客。
你有3个选择:
如果您事先知道了类,请使用switch语句。 基本上是这样的:
switch(str){ case "TypeRepository": return new Repository; }
作为上述的更高级forms,您可以使用字典而不是哈希表
var factory = new Dictionary>(); factory.Add( "TypeRepository", () => new Repository() ); var theObject = factory["TypeRepository"]() as Repository ;
为了获得最大的灵活性,您可以使用reflection在运行时将字符串与类匹配。 请注意,reflection速度相当慢,所以如果你以任何规律性做这件事,你都要避免它。 举个例子,这是一个使用Frans Bouma的方法。 只需在代码中将List<>
更改为Repository<>
:
public static object MakeList( string typeStr ) { var nameSpace = "MyTestApplication" + "."; var objType = Type.GetType(nameSpace + typeStr); // NAMESPACE IS REQUIRED var listType = typeof(List<>).MakeGenericType(objType); return Activator.CreateInstance(listType); } var listOfThings = MakeList("MyCoolObject") as List;
注意:没有办法避免所有这些机制返回object
的事实,然后您必须将其转换为正确的类型,而不是返回仅返回强类型值。
这是不可避免的,因为在运行时你不知道列表的类型,而C#是在编译时知道事物的(这就是人们说“静态类型编程语言”时的意思)。 在C#4中你将会减少痛苦,你将能够返回dynamic
,这将节省你的演员阵容。
Generic类中的“T”代表类型,而不是类型的实例。 因此,如果您希望存储库保存字符串对象,请使用
var _rep = new Repository();