Search code examples
c#reflectionfactoryfactory-pattern

Best approach to instantiate object based on string


I'd like to discuss about the best approach (in C#) to instantiate an object based on an input string. Let me explain. Let'say I have a base class:

public abstract class BaseCar 
{
    public asbtract int GetEngineID();
    //Other stuff...
}

Then I have several implementations of this class, let's say:

public class SportCar : BaseCar
{
    public override int GetEngine()
    { 
      //Specific implementation
    }

}

public class OtherCar: BaseCar
{
    public override int GetEngine()
    { 
      //Specific implementation
    }

}

And so on...

What I'd like to do is to make a static CarFactory class which has a CreateCar method which accepts a string as a parameter and returns a BaseCar instance, depending on what string you give. The string would be a name of a child class.

For example, if I call CarFactory.CreateCar('SportCar') it should return a SportCar instance.

I know I could use a simple switch statement to check which car has been requested and create a new instance based on that but I don't like this approach for two reasons:

  • I plan to have a lot of child classes, hard-coding every case wouldn't be too easy to mantain
  • I plan to implement an inizialization procedure to also give some initial values to the objects I create (using Reflection), so mixing hard-coding and reflection doesn't seem to be a good idea for me.

What I was thinking about is to use the Assembly.CreateInstance from System.Reflection to create an instance of the specified class but since this is the first time I approach this problem, I don't know if there are better ways to do that. Is this a valid approach ?

Considering the input string will come from an XML file, is there a simplier method ? Maybe my issue is already handled in some .NET Assembly which I'm missing.


Solution

  • Here is what I came up with. A generic factory class that automatically registers all types that are a subclass of the given type, and allows you to instantiate them via their name. This is somewhat related to the approach shown in the Java SO question linked by @Achilles in the comments, only that there is no initialisation function associated with the type.

    There is no need to maintain an enum/switch combination of all types. It should also be somewhat easily extendable to handle your proposed reflection based initialisation.

    static class StringFactory<T> where T : class
    {
        static private Dictionary<string, Type> s_dKnownTypes = new Dictionary<string, Type>();
    
        static StringFactory()
        {
            RegisterAll();
        }
    
        static private void RegisterAll()
        {
            var baseType = typeof(T);
            foreach (var domainAssembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in domainAssembly.GetTypes()
                    .Where(t => t.IsSubclassOf(baseType)))
                {
                    s_dKnownTypes.Add(type.Name, type);
                }
            }
        }
    
        static public T Create(string _sTypeName)
        {
            Type knownType;
            if (s_dKnownTypes.TryGetValue(_sTypeName, out knownType))
            {
                return (T)Activator.CreateInstance(knownType);
            }
    
            throw new KeyNotFoundException();
        }
    }
    

    Assuming the classes of your question exist, you would instantiate a specific car like this:

    var car = StringFactory<BaseCar>.Create("SportsCar");
    DoSomethingWith(car.EngineID());
    

    Since your question was for a discussion about the best approaches, please consider this only one of them. I have not used this in a production environment, and it is entirely possible that it is the wrong approach to your specific situation. It works well enough to show the general principle, however, and should provide a starting point for further discussion.