C#

时间:2018-12-19 19:13:18

标签: java c# reflection polymorphism factory-pattern

我试图在C#中创建一个工厂类,该工厂类返回的对象是或扩展了某种基本类型。每当我在工厂中调用getInstance()时,都需要实例化此类型的新实例,因此,我真的只想接受并存储该类型本身。在Java中,我使用Class<? extends Base>来保存要创建的类,然后在其上调用getInstance()

我了解如何在C#中使用Activator类从System.Type创建新对象,但是我不确定的部分是对类类型的约束。我希望只能接受属于或扩展基类的Type。我意识到我可以在工厂中将设置器更改为接受基本类型的实际对象,然后从中检索类型,但是我真的不想实例化整个对象只是为了检索Type变量。

下面是一个小示例Java程序,目的是演示我的需求,以防万一我的问题不清楚。在C#中有什么方法可以做到这一点?

class Program {
   public static void main(String[] args) throws InstantiationException, IllegalAccessException {
         Factory.setClass(Base.class);
         Base b = Factory.getInstance();
         b.Print();

         Factory.setClass(Child.class);
         b = Factory.getInstance();
         b.Print();
      }
}

class Factory {
   private static Class<? extends Base> type;

   // What I want to do in C#.
   public static void setClass(Class<? extends Base> newType) {
      type = newType;
   }

   // What I don't want to have to do in C#.
   public static void setClassFromObject(Base newTypeObject) {
      type = newTypeObject.getClass();
   }

   public static Base getInstance() throws InstantiationException, IllegalAccessException {
      return type.newInstance();
   }
}

class Base {
   public void Print() {
      System.out.println("Hello from base.");
   }
}

class Child extends Base {
   @Override
   public void Print() {
      System.out.println("Hello from child.");
   }
}

2 个答案:

答案 0 :(得分:1)

我看不到如何在编译时强制执行此操作,但是如果您可以通过运行时检查确定,则可以这样做:

class Factory 
{
    private static Type _type;

    public static void SetClass(Type t) 
    {
        if (!(typeof(Base)).IsAssignableFrom(t))
        {
            throw new ArgumentException("type does not extend Base", nameof(t));
        }

        _type = t;
    }

    public static Base GetInstance() 
    {
        return (Base)Activator.CreateInstance(_type);
    }
}

答案 1 :(得分:0)

您可以使“ GetInstance”方法动态化,以便在设置类时也可以设置该方法。这样,您可以在运行时依赖泛型来获取正确的类型。看起来可能像这样:

public class Factory
{
    private static Func<Base> _getInstance;

    //option if you want to pass in an instantiated value
    public static void SetClass<T>(T newType) where T : Base, new()
    {
        _getInstance = () => new T();
    }

    //option if you just want to give it a type
    public static void SetClass<T>() where T : Base, new()
    {
        _getInstance = () => new T();
    }

    public static Base GetInstance()
    {
        return _getInstance();
    }

    //you could just make GetInstance Generic as well, so you don't have to set the class first
    public static Base GetInstance<T>() where T : Base, new()
    {
        return new T();
    }
}