SimpleServiceLocator:为什么单身人士不支持自动构造函数注入?

时间:2010-09-05 15:26:51

标签: c# dependency-injection ioc-container

我一直在试验SimpleServiceLocator,我非常喜欢它,但有一件事我真的很沮丧 - 你不能使用自动构造函数注入单例。更糟糕的是,您甚至无法对其依赖项使用自动构造函数注入。您必须手动创建单例对象,所有依赖项,所有依赖项依赖项等。

为什么SimpleServiceLocator是这样设计的?

除了在第一次请求实例时,该实例是否存储和重用而不是每次创建的新实例时,单个实体是否应该像普通实例一样?为什么SimpleServiceLocator需要在注册过程中提供实例,而不是仅允许在第一次请求时创建和存储实例?

我认为SimpleServiceLocator的重点是没有很多花哨的东西,并且初学者很容易使用,但似乎它设计不正确,并且注册单例的方法应该相同注册常规实例的方法,但方法名称应为RegisterSingle<T>()而不是Register<T>()。有没有理由让我更难复杂(看似不太方便)的设计?

与此同时,我是否可以使用另一个(最好是免费的)IOC容器,让我在代码中注册与SimpleServiceLocator类似的对象,但允许对单例进行自动构造函数注入(或者至少允许自动构造函数注入单例的依赖项) )?

2 个答案:

答案 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的后续调用都必须忽略参数或改变单例的状态,这几乎肯定是一个非常糟糕的主意。