可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question already has an answer here:
-
Converting a string to a class name
2 answers
I have a Generic class like that :
public class Repository<T> {...}
And I need to instance that with a string ...
Example :
string _sample = "TypeRepository";
var _rep = new Repository<sample>();
How can I do that? Is that even possible?
Thanks!
回答1:
Here is my 2 cents:
Type genericType = typeof(Repository<>);
Type[] typeArgs = { Type.GetType("TypeRepository") };
Type repositoryType = genericType.MakeGenericType(typeArgs);
object repository = Activator.CreateInstance(repositoryType);
Answering the question in comment.
MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething");
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something));
closedMethod.Invoke(repository, new[] { "Query String" });
回答2:
First get the Type object using Type.GetType(stringContainingTheGenericTypeArgument)
Then use typeof(Repository<>).MakeGenericType(theTypeObject)
to get a generic type.
And finally use Activator.CreateInstance
回答3:
This is a job for the dynamic
keyword coming in C# 4.0.
There are some nice hacks here that will get you an instance of your object, but no matter what the current version of C# will only know it has an object
, and it can't do much with an object by itself because current C# does not support late binding. You need to be able to at least cast it to a known type to do anything with it. Ultimately, you must know the type you need at compile time or you're out of luck.
Now, if you can constrain your repository class to types implementing some known interface, you're in a much better shape.
回答4:
If I understand your question correctly... What you are trying to do is take your type (Repository<T>) and construct a specific, generic implementation of that at runtime?
If so, take a look at MakeGenericType. You can use typeof(Repository) and the System.Type of the object you want for T and construct it this way. Once you have the type, Activator.CreateInstance will work for you.
回答5:
Assuming that your string holds the name of a type, you can write
object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample)));
However, _rep will be an untyped object, and you will have no way to do anything with it. Since you don't know what type the generic class is at compile time, there is no type that you cast it to. (Unless Repository inherits a non-generic class or implements a non-generic interface)
You could solve that by making a non-generic base class for Repository and casting the object to it.
However, depending on your situation, you probably want to make Repository
a non-generic class.
If Repository
is a class that contains other classes, it's probably best to make it non-generic. You could make its constructor take a Type
object, and then call Type.IsInstanceOfType
on every object you add to make sure it's the right type.
If Repository is a categorized collection of things, it's probably best to make it non-generic, and make its constructor take a string category
.
If you want more specific advice, please post more details about you situation.
回答6:
Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String"));
object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName);
This would create an instance of Repository<string>
. You can replace "System.String" with whatever type you like.
回答7:
Generics work on types, not on instances. You can read more here.
Sounds like you just need to add a constructor to your class that takes in the desired type and initailizes the value:
public class Foo<T>
{
private T = default(T);
public Foo(T initValue)
{
_val = T;
}
}
回答8:
This is "sort of" possible. Sort of in that you can do it, but it's a bit of a hack.
You have 3 options:
If you know your classes up front, use a switch statement. Basically like this:
switch(str){
case "TypeRepository": return new Repository<TypeRepository>;
}
As a more advanced form of the above, you can use a dictionary instead of a hashtable
var factory = new Dictionary<string, Func<object>>();
factory.Add( "TypeRepository", () => new Repository<TypeRepository>() );
var theObject = factory["TypeRepository"]() as Repository<TypeRepository>;
For the greatest flexibility, You can use reflection to match strings to classes at runtime. Be aware that reflection is fairly slow, so if you're doing this with any kind of regularity, you want to avoid it. As an example, Here's one using Frans Bouma's method. Just change List<>
to Repository<>
in your code:
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<MyCoolObject>;
Note: There's no way to avoid the fact that all these mechanisms return object
, which you then have to cast to your proper type, rather than returning just returning strongly typed values.
This is unavoidable, because you don't know the type of the list until runtime, and C# is built around knowing things at compile time (this is what people mean when they say "statically typed programming language"). It will be less painful in C#4, where you'll be able to return dynamic
, which will save you the casting.
回答9:
The "T" in a Generic class stands for a type, not an instance of a type. So if you want your repository to hold string objects, then use
var _rep = new Repository<string>();