如何使用反射动态创建通用 C# 对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1151464/
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-06 09:27:30  来源:igfitidea点击:

How to dynamically create generic C# object using reflection?

c#genericsreflectionactivator

提问by Jeff

In C# I have the following object:

在 C# 中,我有以下对象:

public class Item
{ }

public class Task<T>
{ }

public class TaskA<T> : Task<T>
{ }

public class TaskB<T> : Task<T>
{ }

I want to dynamically create TaskA or TaskB using C# reflection (Activator.CreateInstance). However I wouldn't know the type before hand, so I need to dynamically create TaskA based on string like "namespace.TaskA" or "namespace.TaskAB".

我想使用 C# 反射(Activator.CreateInstance)动态创建 TaskA 或 TaskB 。但是,我事先不知道类型,因此我需要根据“namespace.TaskA”或“namespace.TaskAB”之类的字符串动态创建 TaskA。

采纳答案by JP Alioto

Check out this articleand this simple example. Quick translation of same to your classes ...

看看这篇文章和这个简单的例子。将相同内容快速翻译到您的课程...

var d1 = typeof(Task<>);
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Per your edit: For that case, you can do this ...

根据您的编辑:对于这种情况,您可以这样做......

var d1 = Type.GetType("GenericTest.TaskA`1"); // GenericTest was my namespace, add yours
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

To see where I came up with backtick1 for the name of the generic class, see this article.

要了解我在哪里为泛型类的名称提出了反引号1,请参阅这篇文章

Note: if your generic class accepts multiple types, you must include the commas when you omit the type names, for example:

注意:如果您的泛型类接受多种类型,则在省略类型名称时必须包含逗号,例如:

Type type = typeof(IReadOnlyDictionary<,>);

回答by Ben M

It seems to me the last line of your example code should simply be:

在我看来,您的示例代码的最后一行应该是:

Task<Item> itsMe = o as Task<Item>;

Or am I missing something?

或者我错过了什么?

回答by Jerome Laban

Indeed you would not be able to write the last line.

确实,您将无法写出最后一行。

But you probably don't want to create the object, just for the sake or creating it. You probably want to call some method on your newly created instance.

但是您可能不想创建对象,只是为了或创建它。您可能想在新创建的实例上调用一些方法。

You'll then need something like an interface :

然后,您将需要类似接口的东西:

public interface ITask 
{
    void Process(object o);
}

public class Task<T> : ITask
{ 
   void ITask.Process(object o) 
   {
      if(o is T) // Just to be sure, and maybe throw an exception
        Process(o as T);
   }

   public void Process(T o) { }
}

and call it with :

并调用它:

Type d1 = Type.GetType("TaskA"); //or "TaskB"
Type[] typeArgs = { typeof(Item) };
Type makeme = d1.MakeGenericType(typeArgs);
ITask task = Activator.CreateInstance(makeme) as ITask;

// This can be Item, or any type derived from Item
task.Process(new Item());

In any case, you won't be statically cast to a type you don't know beforehand ("makeme" in this case). ITask allows you to get to your target type.

在任何情况下,您都不会被静态转换为您事先不知道的类型(在本例中为“makeme”)。ITask 允许您获得目标类型。

If this is not what you want, you'll probably need to be a bit more specific in what you are trying to achieve with this.

如果这不是您想要的,您可能需要更具体地了解您要通过此实现的目标。

回答by clemahieu

Make sure you're doing this for a good reason, a simple function like the following would allow static typing and allows your IDE to do things like "Find References" and Refactor -> Rename.

确保您这样做是有充分理由的,像下面这样的简单函数将允许静态类型并允许您的 IDE 执行诸如“查找引用”和重构 -> 重命名之类的操作。

public Task <T> factory (String name)
{
  Task <T> result;

  if (name.CompareTo ("A") == 0)
  {
    result = new TaskA ();
  }
  else if (name.CompareTo ("B") == 0)
  {
    result = new TaskB ();
  }

  return result;
}

回答by A Aiston

I know this question is resolved but, for the benefit of anyone else reading it; if you have all of the types involved as strings, you could do this as a one liner:

我知道这个问题已经解决了,但为了其他人阅读它的利益;如果您将所有涉及的类型都作为字符串,则可以将其作为单行执行:

IYourInterface o = (Activator.CreateInstance(Type.GetType("Namespace.TaskA`1[OtherNamespace.TypeParam]") as IYourInterface);

Whenever I've done this kind of thing, I've had an interface which I wanted subsequent code to utilise, so I've casted the created instance to an interface.

每当我做这种事情时,我都有一个接口,我希望后续代码可以使用它,所以我将创建的实例转换为一个接口。