Search code examples
c#inheritancederived-class

Deciding Which Derived Class To Create Based On A Value?


I was wondering if this is possible or not. I had a number of classes which are all derived from the same base class (BaseClass). When I'm creating an instance, I need to decide which derived class I need to create based on a configuration value. At the moment I'm doing the below but I was hoping there would be a neater way to implement this, that would require less maintenance in case I need to add a new derived class.

BaseClass myclass;
switch (Config.ClassToUse)
{
   case 1: 
        myclass= new DerivedClass1(Config); 
        break;
   case 2: 
        myclass= new DerivedClass2(Config);
        break;
   case 3: 
        myclass = new DerivedClass3(Config);
        break;
}
myclass.DoWork();

The code in the DoWork method varies for each different instance of the class.

Hope that makes sense.


Solution

  • It's Config that knows which class to create and that's why let us Config do its job. We should get rid of magic numbers (what does 2 stand for?) and return Type, not int.

    Quick patch is

    public class Config { 
      ...
      // Get rid of this, move logic into TypeToUse
      public int ClassToUse {get { ... }}
    
      public Type TypeToUse {
        get {
          string name = $"DerivedClass{ClassToUse}";
    
          // Here we scan all types in order to find out the best one. Class must be
          //   1. Derived from BaseClass
          //   2. Not Abstract (we want to create an instance)
          // Among these classes we find the required by its name DerivedClass[1..3]
          // (as a patch). You should implement a more elaborated filter
          // If we have several candidates we'll take the 1st one
          return AppDomain
            .CurrentDomain
            .GetAssemblies()         // scan all assemblies  
            .SelectMany(asm => asm
              .GetTypes()            // and all types 
              .Where(tp => typeof(BaseClass).IsAssignableFrom(tp))) // ... for derived classes
           .Where(tp => !tp.IsAbstract)       //TODO: Add more filters if required
           .Where(tp => tp.Name.Equals(name)) //TODO: put relevant filter here 
           .FirstOrDefault();            
        }
      } 
    
      public BaseClass CreateInstance() {
        Type tp = TypeToUse;
    
        if (tp == null)
          return null; // Or throw exception
    
        return Activator.CreateInstance(tp, this) as BaseType;
      } 
    } 
    

    Then you can put

    BaseClass myclass = Config.CreateInstance();
    
    myclass.DoWork();