基于字符串实例化 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 语句来检查请求了哪辆车并基于它创建一个新实例,但我不喜欢这种方法有两个原因:
- 我计划有很多 child classes,hard-coding 每个
case
都不太容易维护
- 我计划实施一个初始化过程,为我创建的 object 提供一些初始值(使用反射),因此混合 hard-coding 和反射似乎不是一个好主意我的想法。
我想的是使用 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());
由于您的问题是关于最佳方法的讨论,请考虑这只是其中之一。我没有在生产环境中使用过它,并且完全有可能是针对您的特定情况的错误方法。然而,它足以展示一般原则,并且应该为进一步讨论提供一个起点。
我想讨论基于输入字符串实例化 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 语句来检查请求了哪辆车并基于它创建一个新实例,但我不喜欢这种方法有两个原因:
- 我计划有很多 child classes,hard-coding 每个
case
都不太容易维护 - 我计划实施一个初始化过程,为我创建的 object 提供一些初始值(使用反射),因此混合 hard-coding 和反射似乎不是一个好主意我的想法。
我想的是使用 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());
由于您的问题是关于最佳方法的讨论,请考虑这只是其中之一。我没有在生产环境中使用过它,并且完全有可能是针对您的特定情况的错误方法。然而,它足以展示一般原则,并且应该为进一步讨论提供一个起点。