我正在阅读本文以删除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方法。
有人可以提供最好的方法,而不仅仅是解决问题。
答案 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();