Polymorphim:如何创建Interface的动态实例

时间:2016-11-23 10:41:03

标签: c# switch-statement polymorphism

我正在阅读本文以删除c#中的切换案例并使用多态来实现它。

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

这是我的代码:

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");
        }
    }
}

如何为该接口创建实例,以便它可以动态调用该Bark方法。

有人可以提供最好的方法,而不仅仅是解决问题。

3 个答案:

答案 0 :(得分:5)

考虑使用Factory Pattern来实现这一目标,例如:

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

修改

使用IoC容器(本例中为AutoFac)的一种方法是扫描程序集并注册由类名称键入的IAnimal的所有实现(如果您注册了单例实例,则可以通过属性键入接口),如下所示:

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

当然,您仍然可以将其重构为工厂 PS。使用AutoFac的Assembly Scanning可能有更好的方法在程序集中注册所有类型,但我无法找到任何方法将其与每种类型的键组合。

答案 1 :(得分:1)

在最简单的情况下(仅对string接口实现对应IAnimal),您可以使用字典

  private static Dictionary<String, Func<IAnimal>> s_Factory = 
    new Dictionary<string, Func<IAnimal>>(StringComparer.OrdinalIgnoreCase) {

    {"dog", () => new Dog()},
    {"hound", () => new Dog()}, // synonym to dog
    {"cat", () => new Cat()}    
  };

所以你把

  static void Main(string[] args)
  {
      var animalType = "Dog";

      // if we sure that animal exists we can just call the dictionary 
      IAnimal animal = s_Factory[animalType]();

      animal.Bark();
  }

  static void Main(string[] args)
  {
      Console.WriteLine("Enter animal type, please");
      String animalType = Console.ReadLine();

      Func<IAnimal> maker; 

      // If we not sure that animal exists, we have to check
      if (s_Factory.TryGetValue(animalType, out maker)) 
        maker().Bark();
      else
        Console.WriteLine("Sorry, I don't know such an animal"); 
  }

然而,在一般的情况下,你必须实现工厂模式(参见KMoussa的回答)

答案 2 :(得分:0)

对于这类问题,通常我使用Activator.CreateInstance()方法,但正如KMoussa所说,使用一个工厂来处理IAnimal具体类的创建是一种很好的(并且是推荐的)方法。这是我的一个项目的例子,有点类似。

public class ItemFactory
{
    /*
     * Singleton pattern for factory, cause generally a factory only has one instance through out the app.
     */
    private static ItemFactory itemFactory;
    public static ItemFactory Instance
    {
        get
        {
            if (itemFactory == null)
                itemFactory = new ItemFactory();
            return itemFactory;
        }
    }

    // method to create an instance of IItem, note the static modifier if you want to call it from base class, if
    // you use singleton, ignore static.
    public /*static*/ IItem CreateItem(string itemType)
    {
         Type type = Type.GetType(itemType);
         var temp = Activator.CreateInstance(type);
         return temp;
    }
}

以后你可以,

var a = ItemFactory.Instance.CreateItem("RawItem");
a.RawItemMethod1();
a.RawItemMethod2();