【问题标题】:How to dynamically create generic C# object using reflection? [duplicate]如何使用反射动态创建通用 C# 对象? [复制]
【发布时间】:2010-11-12 04:41:14
【问题描述】:

在 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。

【问题讨论】:

    标签: c# generics reflection activator


    【解决方案1】:

    看看这个article 和这个simple example。将相同的内容快速翻译成您的课程...

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

    要查看我在哪里想出反引号 1 作为泛型类的名称,请参阅 this article

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

    Type type = typeof(IReadOnlyDictionary<,>);
    

    【讨论】:

    • 是否需要反引号,即如果省略,编译器是否假定它为 1?
    • 感谢您链接到我的博客文章“使用反射在 C# .Net 中实例化泛型类”(omegacoder.com/?p=38) 作为“简单示例”。 :-) 我很高兴这篇文章找到了新的生活。
    • 如何使用is关键字比较泛型类型?
    • 您也可以使用 Activator.CreateInstance,这样可以消除创建指定“typeargs”并自己制作泛型类型的需要。
    • Francis Dean,关键是你有 Type 对象,你不能把它放在
    【解决方案2】:

    确实,你不能写最后一行。

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

    然后您将需要类似界面的东西:

    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 允许您到达您的目标类型。

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

    【讨论】:

    • 我在 Task 中有类似 Process() 的东西。在我的情况下,我实际上不再关心最后一行,正如你所说我只是调用 task.Process() 因此是否能够对最后一行进行编码变得不敬。
    【解决方案3】:

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

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

    还是我错过了什么?

    【讨论】:

    • 您没有遗漏任何东西。是我想的不对。我昨晚不应该喝那么多酒!
    【解决方案4】:

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

    【讨论】:

    • 为什么要使用 .CompareTo?为什么不 == 或 .Equals(如果您想要更多控制权)。也许换个开关会更好。
    • 我从未检查过 C# 是否对 == 进行字符串比较而不是参考比较。如果正确实现,CompareTo 和 Equals 应该具有相同的运行时效率。 switch 块不会有与将整数放入 switch 块相同的加速。它会编译成一个 if-else 块。
    【解决方案5】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-13
      • 2015-06-02
      • 1970-01-01
      • 2023-03-19
      • 2012-05-15
      相关资源
      最近更新 更多