Ian Boggs
Ian Boggs

Reputation: 542

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.

Upvotes: 4

Views: 465

Answers (2)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186803

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

Upvotes: 2

Prodigle
Prodigle

Reputation: 1797

Have Config.ClassToUse return a Type of your derived class rather than an integer identifying it.

Then your code can be shortened to:

BaseClass myclass = System.Activator.CreateInstance(Config.ClassToUse)

Upvotes: 0

Related Questions