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

发布于 2024-07-26 23:30:25 字数 353 浏览 10 评论 0原文

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

public class Item
{ }

public class Task<T>
{ }

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

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

我想使用 C# 反射 (Activator.CreateInstance) 动态创建 TaskA 或 TaskB。 但是我事先不知道类型,所以我需要基于“namespace.TaskA”或“namespace.TaskAB”等字符串动态创建TaskA。

In C# I have the following object:

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".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

枕花眠 2024-08-02 23:30:25

请查看这篇文章和这篇简单示例。 将相同内容快速翻译为您的类...

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

根据您的编辑:对于这种情况,您可以执行此操作...

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

要查看我在哪里为通用类名称使用 backtick1,请参阅 本文

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

Type type = typeof(IReadOnlyDictionary<,>);

Check out this article and 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.

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<,>);
薯片软お妹 2024-08-02 23:30:25

事实上,你将无法写出最后一行。

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

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

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) { }
}

并使用 : 调用它。

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());

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

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

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.

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.

意犹 2024-08-02 23:30:25

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

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

或者我错过了什么?

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?

≈。彩虹 2024-08-02 23:30:25

确保您这样做是有充分理由的,像下面这样的简单函数将允许静态类型,并允许您的 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;
}

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.

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;
}
二智少女猫性小仙女 2024-08-02 23:30:25

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

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

每当我完成此类操作时,我都会有一个接口,我希望后续代码能够使用该接口,因此我将创建到接口的实例。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文