我正在编写一个库,为其消费者提供一系列公共类型.
我想从这个库依赖注入友好的类型.这意味着每个类都需要一个构造函数,通过该构造函数可以指定要初始化的对象的每个依赖项.我还希望库遵循约定优于配置原则.这意味着如果消费者想要默认行为,他可以使用无参数构造函数,并且该对象将以某种方式为自己构造依赖关系.
在示例(C#)中:
public class Samurai {
private readonly IWeapon _weapon;
// consumers will use this constructor most of the time
public Samurai() {
_weapon = ??? // get an instance of the default weapon somehow
}
// consumers will use this constructor if they want to explicitly
// configure dependencies for this instance
public Samurai(IWeapon weapon) {
_weapon = weapon;
}
}
Run Code Online (Sandbox Code Playgroud)
我的第一个解决方案是使用服务定位器模式.
代码如下所示:
...
public Samurai() {
_weapon = ServiceLocator.Instance.Get<IWeapon>();
}
...
Run Code Online (Sandbox Code Playgroud)
不过,我对此有疑问.服务定位器已被标记为反模式(链接),我完全同意这些参数.另一方面,Martin Fowler主张在这种情况下准确使用服务定位器模式(库项目)(链接).我想要小心并消除在显示服务定位器确实是个坏主意后重写库的可能性. …
dependency-injection inversion-of-control default-value service-locator
从 .NET 4.0 开始,自动生成的添加/删除事件处理程序是线程安全的(此处和此处)。因此,将其侦听器注册到公开事件的客户端可以从多个线程同时进行,而不会发生竞争。
但是如果我想以线程安全的方式触发事件怎么办?推荐的做法似乎如下(这里):
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
EventHandler myEvent = MyEvent;
if (myEvent != null)
{
myEvent(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,在阅读了有关 .NET 内存模型的内容后(例如 MSDN 杂志2012-12和2013-01),我不再认为这是正确的。我担心的是编译器可能会引入内存读取,因此上面的代码可能会被 JIT-ted 变成这样:
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
// JIT removed the local variable and introduced two memory reads instead.
if (MyEvent != null)
{
// A race condition may cause the following line to …Run Code Online (Sandbox Code Playgroud)