如何从 C# 中的字符串创建泛型类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/687363/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 13:31:32  来源:igfitidea点击:

How do I create a generic class from a string in C#?

c#.netreflectiongenerics

提问by Paul

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!

谢谢!

采纳答案by alex

Here is my 2 cents:

这是我的 2 美分:

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" });

回答by David

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

泛型类中的“T”代表类型,而不是类型的实例。因此,如果您希望您的存储库保存字符串对象,请使用

var _rep = new Repository<string>();

回答by Frans Bouma

First get the Type object using Type.GetType(stringContainingTheGenericTypeArgument)

首先使用获取 Type 对象 Type.GetType(stringContainingTheGenericTypeArgument)

Then use typeof(Repository<>).MakeGenericType(theTypeObject)to get a generic type.

然后使用typeof(Repository<>).MakeGenericType(theTypeObject)获取泛型类型。

And finally use Activator.CreateInstance

最后使用 Activator.CreateInstance

回答by Orion Edwards

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:

您有 3 个选择:

If you know your classes up front, use a switch statement. Basically like this:

如果您预先了解您的课程,请使用 switch 语句。基本上是这样的:

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'smethod. Just change List<>to Repository<>in your code:

为了获得最大的灵活性,您可以使用反射在运行时将字符串与类进行匹配。请注意,反射相当慢,因此如果您有规律地这样做,则要避免它。例如,这是一个使用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<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.

注意:无法避免所有这些机制都返回的事实,object然后您必须将其强制转换为正确的类型,而不是仅返回强类型值。

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.

这是不可避免的,因为直到运行时您才知道列表的类型,而 C# 是围绕在编译时了解事物而构建的(这就是人们所说的“静态类型编程语言”的意思)。在 C#4 中它不会那么痛苦,在那里你可以返回dynamic,这将节省你的转换。

回答by Daniel

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;
    }
}

回答by Reed Copsey

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?

如果我正确理解你的问题......你想要做的是获取你的类型(Repository<T>)并在运行时构造一个特定的通用实现?

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.

如果是这样,请查看MakeGenericType。您可以使用 typeof(Repository) 和您想要的对象的 System.Type T 并以这种方式构造它。一旦你有了类型, Activator.CreateInstance 就会为你工作。

回答by Matt Brindley

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.

这将创建一个Repository<string>. 您可以用您喜欢的任何类型替换“System.String”。

回答by SLaks

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)

然而,_rep 将是一个无类型的对象,你将无法对它做任何事情。由于您在编译时不知道泛型类是什么类型,因此没有将其强制转换为的类型。(除非 Repository 继承了非泛型类或实现了非泛型接口)

You could solve that by making a non-generic base class for Repository and casting the object to it.

您可以通过为 Repository 创建一个非通用基类并将对象转换为它来解决这个问题。

However, depending on your situation, you probably want to make Repositorya non-generic class.

但是,根据您的情况,您可能想要创建Repository一个非泛型类。

If Repositoryis a class that contains other classes, it's probably best to make it non-generic. You could make its constructor take a Typeobject, and then call Type.IsInstanceOfTypeon every object you add to make sure it's the right type.

如果Repository是包含其他类的类,最好将其设为非泛型。你可以让它的构造函数接受一个Type对象,然后调用Type.IsInstanceOfType你添加的每个对象以确保它是正确的类型。

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.

如果 Repository 是一个分类的事物集合,那么最好将其设为非泛型,并使其构造函数采用string category.

If you want more specific advice, please post more details about you situation.

如果您需要更具体的建议,请发布有关您情况的更多详细信息。

回答by Joel Coehoorn

This is a job for the dynamickeyword coming in C# 4.0.

这是dynamicC# 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 timeor you're out of luck.

这里有一些不错的技巧可以为您提供对象的实例,但是无论当前版本的 C# 是什么,它都只会知道它有一个object,并且它本身不能对对象做太多事情,因为当前的 C# 不支持后期绑定。您至少需要能够将其转换为已知类型才能对其进行任何操作。最终,您必须知道在编译时需要的类型否则您就不走运了。

Now, if you can constrain your repository class to types implementing some known interface, you're in a much better shape.

现在,如果您可以将存储库类限制为实现某些已知接口的类型,那么您的状态会好得多。