我有一个使用构造函数注入的类。
public class MyClass
{
public MyClass(IInterface1 interface1)
{
}
public Dispose()
{
interface1.dispose();
}
}
接口1将由DI注入。但有时,我需要手动创建MyClass。
public class MyOtherClass
{
private readonly IInterface1 _interface1;
public MyOtherClass()
{
_interface1 = new Interface1();
}
public Foo()
{
foo = new MyClass(_interface1);
bar = new MyClass(_interface1);
}
}
在我的dispose方法中,当MyClass被销毁时,它总是处理掉。问题是,interface1由MyOtherClass拥有,可能仍然被其他实例使用,不应该被处理掉。我怎么解决这个问题?
答案 0 :(得分:3)
你不应该打电话
interface1.dispose();
在MyClass中。
如果DI容器创建了interface1,它将在那里发布。 如果您在MyOtherClass中显式创建它,请在MyOtherClass中将其公开。
答案 1 :(得分:3)
您的代码存在两个问题。
首先,MyClass
'非法'获取IInterface1
的所有权,而不知道该实例的生命周期是什么。这意味着如果重用IInterface1
,系统就会中断。
所有权的基本规则是“创建实例的人负责处理它”(RAII idiom)。由于MyClass
没有创建IInterface1
,因此他不应该将其丢弃。因此,MyClass
不应实施Dispose
方法,也不应调用IInterface1.Dispose()
。
其次,通过让IInterface1
实施IDisposable
,您的代码违反了Dependency Inversion Principle(DIP),因为DIP声明:
抽象不应该依赖于细节。细节应该取决于 抽象。
但是,IInterface1
取决于实现细节,因为某个组件是否具有需要处理的非托管资源是实现细节。 IInterface1
的每个实现都不太可能总是需要处理资源,因此您的接口会泄漏特定实现的实现细节。
因此,不要让IInterface1
实现IDisposable
,而只是让给定的实现实现IDisposable
。这样做的好处在于它最小化了IInterface1
的API,这使得它更容易使用,并且可能允许您符合Interface Segregation Principle。
如果你这样做,问题会立即消失,因为MyClass
不知道是否可以处置IInterface1
(这是好的),这意味着它不会意外地打电话首先是Dispose
。
这当然会将该实例的处理留给创建该实例的系统部分(这很好)。如果此实例是由DI库(如果您使用的话)代表您创建的,则DI库通常负责处理该实例。如果你不使用容器,你必须确保自己处理。
请注意,并非所有容器都跟踪所有实例。例如,Unity和Simple Injector不会自动跟踪(和处置)瞬态实例。然而,在大多数情况下,一次性组件应该以规范的生活方式注册(根据网络请求或类似的东西)。我认为在这种情况下,所有容器都会处理以这种生活方式注册的实例。