4

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.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Andrew
  • 58
  • 1
  • 5
  • 1
    https://msdn.microsoft.com/en-us/library/d133hta4(v=vs.110).aspx – Mike Cheel May 02 '17 at 12:58
  • Same query here (for java).. should probably provide some pointers: http://stackoverflow.com/questions/3434466/creating-a-factory-method-in-java-that-doesnt-rely-on-if-else – Faheem May 02 '17 at 13:03

1 Answers1

1

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.

Community
  • 1
  • 1
jcb
  • 382
  • 3
  • 17
  • Thank you for this idea, actually I think registering all available types once and then using a data structure to map them is nice approach, rather than looking for then specified type in the assembly everytime an instance is reequested. – Andrew May 03 '17 at 08:13