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

时间:2017-05-02 12:56:28

标签: c# reflection factory factory-pattern

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

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

然后我有几个这个类的实现,让我们说:

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

}

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

}

等等......

我想要做的是创建一个静态CarFactory类,它具有CreateCar方法,该方法接受string作为参数并返回BaseCar实例,具体取决于你给的字符串。该字符串将是子类的名称。

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

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

  • 我计划有很多子课程,每个case的硬编码都不会太容易保留
  • 我计划实现一个inizialization过程,也为我创建的对象(使用Reflection)提供一些初始值,因此混合硬编码和反射对我来说似乎不是一个好主意。

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

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

1 个答案:

答案 0 :(得分:0)

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

无需维护所有类型的枚举/开关组合。它也应该可以轻松扩展,以处理您提出的基于反射的初始化。

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

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

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

由于您的问题是关于最佳方法的讨论,请仅考虑其中一种。我没有在生产环境中使用它,并且完全有可能对您的具体情况采用错误的方法。然而,它足以表明一般原则,并应为进一步讨论提供一个起点。