我一直在试验SimpleServiceLocator,我非常喜欢它,但有一件事我真的很沮丧 - 你不能使用自动构造函数注入单例。更糟糕的是,您甚至无法对其依赖项使用自动构造函数注入。您必须手动创建单例对象,所有依赖项,所有依赖项依赖项等。
为什么SimpleServiceLocator是这样设计的?
除了在第一次请求实例时,该实例是否存储和重用而不是每次创建的新实例时,单个实体是否应该像普通实例一样?为什么SimpleServiceLocator需要在注册过程中提供实例,而不是仅允许在第一次请求时创建和存储实例?
我认为SimpleServiceLocator的重点是没有很多花哨的东西,并且初学者很容易使用,但似乎它设计不正确,并且注册单例的方法应该相同注册常规实例的方法,但方法名称应为RegisterSingle<T>()
而不是Register<T>()
。有没有理由让我更难复杂(看似不太方便)的设计?
与此同时,我是否可以使用另一个(最好是免费的)IOC容器,让我在代码中注册与SimpleServiceLocator类似的对象,但允许对单例进行自动构造函数注入(或者至少允许自动构造函数注入单例的依赖项) )?
答案 0 :(得分:2)
RegisterSingle<T>
方法只是一种花哨的辅助方法,只是为了让生活更轻松。您可以使用RegisterSingle<T>
方法完成Register<T>
的操作。 The web site gives examples of this。您可以使用Register<T>
方法注册单个实例,如下所示(它使用闭包):
var weapon = new Katana();
container.Register<IWeapon>(() => weapon);
当您查看网站上的lifestyle management examples时,您可以看到以下用于创建线程静态实例的示例:
[ThreadStatic]
private static IWeapon weapon;
container.Register<IWeapon>(
() => return weapon ?? (weapon = new Katana()));
我认为这是简化的力量,因为这种模式几乎没有任何你无法做到的事情。你要努力实现的目标有点困难,我必须承认这一点,但没有什么能真正推进IMO。以下是解决问题所需的代码:
private static IWeapon weapon;
container.Register<IWeapon>(
() => weapon ?? (weapon = container.GetInstance<Katana>()));
这里的技巧是将实例存储在静态变量中(就像线程静态一样),但是现在你不应该通过new
自己创建实例,而是将创建委托给简单的服务定位器。这是有效的,因为 - 如你所知 - 当请求具体类型时,SimpleServiceLocator将执行自动构造函数注入。
我必须承认,我们需要做这个诡计是一种耻辱。如果图书馆真的可以为我们做这件事会很好。例如,我可以想象添加了RegisterSingle<T>
重载,允许我们执行以下操作:
container.RegisterSingle<IWeapon>(
() => container.GetInstance<Katana>());
请让我知道您对这种超负荷的看法。我总是对反馈感兴趣,以使图书馆更好。对于下一个版本来说,这肯定是一个很好的功能。
更新
自0.14版本开始,我们可以执行以下操作:
container.RegisterSingle<IWeapon, Katana>();
它不会比这更容易。
干杯
答案 1 :(得分:0)
典型的单例实现具有private
构造函数,因此容器无法“看到”它,调用它或检测依赖项。
也许您指的是某些IoC容器的生命周期管理功能,您可以将容器配置为始终返回同一个类的单个实例。
这不是单身人士的意思。虽然容器返回相同的实例,但没有什么可以阻止您使用new
在代码中实例化实例。
另一方面,单例只能从任何源实例化一次(在某些实现中每个线程一次)。它不公开公共构造函数,而是公开构造函数,例如:
public class MySingleton
{
// note: not a thread-safe implementation
static MySingleton instance;
static DependencyThing thing;
private MySingleton(DependencyThing thing)
{
MySingleton.thing = thing;
}
public static MySingleton GetMySingleton(DependencyThing thing)
{
if(instance == null) instance = new MySingleton(thing);
return instance;
}
}
正如您所看到的,您无法从课堂外调用new MySingleton()
。要“实例化”MySingleton,您必须调用MySingleton.GetMySingleton(thing)
。此调用返回唯一的实例或创建,然后返回它。
SimpleServiceLocator
无法知道如何创建此对象,也无法知道如何检测其依赖项。
如果API暴露了类似
的内容,则可以添加此功能public void Register<T>(Expression<Func<T>> staticFactoryMethod)…
...在这种情况下你可以调用Register(() => MySingleton.GetMySingleton());
,但这只能在没有参数的情况下工作。必须有更多的重载:
public void Register<T, TParam1>(Expression<Func<TParam1, T>> staticFactoryMethod)…
public void Register<T, TParam1, TParam2>(Expression<Func<TParam1, TParam2, T>> staticFactoryMethod)…
...以便容器知道要实例化的依赖项并传递给指定的工厂方法。
所有这一切,对单例进行依赖注入并没有多大意义。每次对GetMySingleton
的后续调用都必须忽略参数或改变单例的状态,这几乎肯定是一个非常糟糕的主意。