C# 构造函数注入对象的处理

C# 构造函数注入对象的处理,c#,winforms,dependency-injection,idisposable,C#,Winforms,Dependency Injection,Idisposable,假设我有一个类与另一个类关联。它看起来类似于以下内容: public class DisposableClassOne : IDisposable { private class mDisposableClassTwo; public DisplosableClassOne(DisposableClassTwo dcTwoInjected) { mDisposableClassTwo = dcTwoInjected; } public void Di

假设我有一个类与另一个类关联。它看起来类似于以下内容:

public class DisposableClassOne : IDisposable
{
   private class mDisposableClassTwo;

   public DisplosableClassOne(DisposableClassTwo dcTwoInjected)
   {
      mDisposableClassTwo = dcTwoInjected;
   }

   public void Dispose()
   {
      // Should I dispose here? or make caller dispose of dcTwoInjected
      //mDisposableClassTwo.Dispose();
   }
}
我应该调用
mDisposableClassTwo
Dispose
方法,还是让调用者这样处理

using(DisposableClassTwo dcTwoInjected = new DisposableClassTwo())
using(DisposableClassOne dcOne = new DisposableClassOne(dcTwoInjected))
{
   // do stuff with dcOne
}

我认为让调用方处理它是最好的方法,但是我认为通过将调用放入Dispose方法,它可以保证调用它。是否有更好的方法来处理此问题?

最好不要处理任何外部引用,这些引用将由调用方自己自动处理

请参阅此线程,其中建议仅处理成员变量:


我熟悉的标准是使用一次性模式。有一个虚拟的
Dispose(bool disposing
)方法。
Dispose()
方法调用
Dispose(true)
,终结器调用
Dispose(false)

然后,在主要处理方法中:

if (disposing)
{
    // Dispose of the referenced object, as well.
}
StreamWriter和StreamReader遵循此模式。如果您明确地调用
Dispose()
,它们也会处理底层
。如果您让终结器进行清理,他们将不进行清理

或者,您可以添加一个属性:
DisposeChild
,如果为true,则处理子对象,如果不是,则不处理它


我也同意瑞文的观点。几乎在所有情况下,对象都应该在实例化的同一范围内进行处理。

我认为这是您在设计中必须做出的终身决定

DisposableClassOne是一次性的,因为它引用了DisposableClass2吗

以及

可处置类2的生命周期是否独立于可处置类1

对我来说,这两个问题的答案在每个课堂设计中都有所不同。StreamReader/Writer是一个很好的例子,第一个问题是“是”,第二个问题是“否”——一旦读者使用了StreamReader中的流,没有人会期望它被使用,所以读者会处理它


但是,如果DisposableClass2是其他资源——可能是一个文件引用,您依次传递给多个类来“做点什么”。在这种情况下,您不希望在准备就绪之前将其释放,而DisposableClassOne可能早已过时。

如果您正在创建的类在逻辑上拥有(1)构造函数注入的资源,则它应该在内部将其释放。如果它不拥有资源,那么它应该什么也不做,并且依赖消费者来决定何时应该处置它

如果您的类拥有对非托管资源的引用,您可能还需要实现一个终结器(析构函数),因为不能保证任何人都会调用您的
Dispose
方法

通常,您希望避免调用方必须决定何时处理传递给类构造函数的对象的情况。调用者有可能过早地处置资源(或者超过必要的时间保留资源)。在某些设计中,这并不总是一件容易的事情。。。可悲的是,一次性对象模式并不总是与自身完美结合



(1) 所谓所有权,我的意思是,您的类控制它所传递的资源的生命周期,并且没有其他代码持有对它的引用。如果任何其他代码(其生命周期与您的类的生命周期无关)持有对该资源的引用并将独立使用它,则您不是该资源的所有者。

要正确使用IDisposable对象,必须遵循“如果最后一个代码熄灭,请关灯”。有几个不错的场景,还有一个令人讨厌的场景:

  • 对象的创建者将其提供给另一个对象(“收件人”),但第一个对象的创建者控制收件人的生命周期,并且可以知道收件人何时不再需要传递的对象。在这种情况下,原始对象的创建者负责Dispose。
  • 对象的创建者将其交给接收者,接收者知道一旦对象被移交,其他人将不会使用该对象。在这种情况下,收件人将在处理完对象后调用Dispose。
  • 对象接收者有时可能以与#1匹配的方式使用,有时也可能以与#2匹配的方式使用,但对象的供应商将知道何时给出适用的情况。这种情况可以通过告诉接收者接收者是否应该处置所给予的对象来处理。
  • 供应商和接收者都希望使用该对象,但可能不知道哪个会比另一个更长寿。这是最困难的情况。 我倾向于喜欢第3条中建议的方法。但有时情况4也适用。一种处理方法是对象的接收者在处理完对象后触发事件。该事件可以互锁。递减计数器,指示有多少对象仍在使用传递的对象。一旦计数器为零,对象可能会被删除。

    所有者清理的概念太普遍了,不能忽略。所有权必须不仅仅是巧合

    如果您想要更易于维护的软件,请避免可能被错误解释的副作用。Stream vs StreamReader示例是一个很好的示例。。。两者都是可识别的,作为来电者,我将两者都设置为可识别的,因此我决不允许自己处理它们中的任何一个。这样做会违反抽象,将调用代码的知识与流类实现的细节联系起来(这可能会随着时间的推移而改变,学术上)


    最简单的答案是使用两个usings(),即使您随后去制作(并记录!),第二个IDisposable将处理第一个IDisposable。

    我使用的策略是由创建对象的人负责处理。如果没有,那么您将依赖于大量关于如何使用对象的假设