简体   繁体   中英

Parameterized System.Type Variable in C#

I am trying to make a factory class in C# that returns objects that are or extend a certain base type. I need to instantiate a new instance of this type every time I call getInstance() on the factory, so I really only want to accept and store the type itself. In Java I have used the Class<? extends Base> Class<? extends Base> to hold the class to create and then called getInstance() on it.

I understand how to use the Activator class in C# to create new object from a System.Type but the part I'm not sure about is the constraint on the class type. I want to be able to only accept Types that are or extend the base class. I realize that I could change the setter on the factory to accept an actual object of the base type then retrieve the type from it, but I didn't really want to instantiate an entire object just to retrieve a Type variable.

Below is a little example Java program just to demonstrate what I need, in case my question is not clear. Is there any way to do this in 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.");
   }
}

I don't see how to enforce this at compile time, but if you're OK with a runtime check, you could do it like this:

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

You could make the "GetInstance" method dynamic, so that when you set the class, you also set the method. That way you could rely on generics at runtime to get you the right type. It could look like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM