基于字符串实例化 object 的最佳方法

Best approach to instantiate object based on string

我想讨论基于输入字符串实例化 object 的最佳方法(在 C# 中)。让我解释。 假设我有一个基础 class:

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

然后我有这个class的几个实现,比方说:

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

}

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

}

等等...

我想做的是制作一个静态 CarFactory class,它有一个 CreateCar 方法,该方法接受 string 作为参数,并且 return 是一个BaseCar 个实例,具体取决于您提供的字符串。该字符串将是 child class.

的名称

例如,如果我调用 CarFactory.CreateCar('SportCar') 它应该 return 一个 SportCar 实例。

我知道我可以使用一个简单的 switch 语句来检查请求了哪辆车并基于它创建一个新实例,但我不喜欢这种方法有两个原因:

我想的是使用 System.Reflection 中的 Assembly.CreateInstance 创建指定 class 的实例,但由于这是我第一次处理这个问题,所以我不知道是否有更好的方法来做到这一点。这是一种有效的方法吗?

考虑到输入字符串将来自 XML 文件,是否有更简单的方法?也许我的问题已经在我遗漏的某些 .NET 程序集中得到处理。

这是我想到的。一个通用工厂 class,它自动注册所有属于给定类型的子 class 类型,并允许您通过它们的名称实例化它们。这与评论中@Achilles 链接的 the Java SO question 中显示的方法有些相关,只是没有与该类型关联的初始化函数。

无需维护所有类型的 enum/switch 组合。它还应该在某种程度上易于扩展,以处理您建议的基于反射的初始化。

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

假设您的问题 classes 存在,您将像这样实例化一辆特定的汽车:

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

由于您的问题是关于最佳方法的讨论,请考虑这只是其中之一。我没有在生产环境中使用过它,并且完全有可能是针对您的特定情况的错误方法。然而,它足以展示一般原则,并且应该为进一步讨论提供一个起点。