如何确保不处置其他人仍在使用的对象

时间:2014-11-05 09:10:23

标签: c# .net dependency-injection dispose

我有一个使用构造函数注入的类。

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拥有,可能仍然被其他实例使用,不应该被处理掉。我怎么解决这个问题?

2 个答案:

答案 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不会自动跟踪(和处置)瞬态实例。然而,在大多数情况下,一次性组件应该以规范的生活方式注册(根据网络请求或类似的东西)。我认为在这种情况下,所有容器都会处理以这种生活方式注册的实例。