Karthik Chintala Karthik Chintala - 10 days ago 5
C# Question

Polymorphim: How to create dynamic instance of Interface

I'm going through this article to remove those switch case in c# and use polymorphism to achieve it.

https://refactoring.guru/replace-conditional-with-polymorphism

Here is my code though:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Consolas
{
public class Program
{
static void Main(string[] args)
{
var animalType = "Dog"; //consider this as an argument supplied to the app
//How to create dynamic instance for IAnimal ???
//Is Activator.CreateInstance the only way to achieve it ??
//IAnimal animal = ..what here ?
//animal.Bark();
}
}

public interface IAnimal
{
void Bark();
}

public class Dog: IAnimal
{
public void Bark()
{
Console.WriteLine("Bow Bow");
}
}

public class Cat: IAnimal
{
public void Bark()
{
Console.WriteLine("Meow Meow");
}
}
}


How do I create instance for that Interface so that it can invoke that Bark method dynamically.

Can someone provide the best way of doing not just a work around.

Answer

Consider using the Factory Pattern to achieve that, an example would be something like this:

public class AnimalFactory
{
    public IAnimal CreateAnimal(string animalType)
    {
         //Here you can either have a switch statement checking for
         //type, or use Type.GetType(animalType) and then create an
         //instance using the Activator - but in the latter case you will
         //need to pass in the exact type name of course 

         //PS. You can also use an IoC container to resolve all
         //implementations of IAnimal and have a distinguishing property
         //that you use here to select the type you want, but I think
         //that's a bit off topic so won't detail it here
    }
}

 static void Main(string[] args)
 {
        var animalType = "Dog";
        var amimal = new AnimalFactory().CreateAnimal(animalType);
        animal.Bark(); 
 }

EDIT

One way to use an IoC container (AutoFac in this example) is to scan you assemblies and register all implementations of IAnimal keyed by the class name (if you were registering singleton instances, you could key by a property of the interface), something like the following:

class Program
{
    public static IContainer _container;

    static void Main(string[] args)
    {
        //Register types
        Register();

        //Resolve a dog
        var dog = _container.ResolveKeyed<IAnimal>("Dog");
        //Resolve a cat
        var cat = _container.ResolveKeyed<IAnimal>("Cat");

        dog.Bark(); //Bow Bow
        cat.Bark(); //Meow Meow

        Console.Read();
   }

    static void Register()
    {
        //Get all types implementing IAnimal, you can of course scan multiple assemblies,
        //here I am only looking at the current assembly
        var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => !t.IsInterface && t.IsAssignableTo<IAnimal>());

        var builder = new ContainerBuilder();
        foreach (var t in types)
        {
            //Use the type name as a key to the instance
            builder.RegisterType(t).Keyed<IAnimal>(t.Name)
              .InstancePerDependency(); //You want a new instance each time you resolve
        }          

        _container = builder.Build();            
    }
}

Of course you can still refactor that into a factory PS. There might be a better way of registering all types within the assembly using AutoFac's Assembly Scanning, but I couldn't see any way to combine that with a key per type.

Comments