简体   繁体   English

使用一次性对象的类的层次结构。在所有这些上实现IDisposable?

[英]Hierarchy of classes that use a disposable object. Implement IDisposable on all of them?

I have a class that uses a filestream. 我有一个使用文件流的类。 It needs to close the stream when the app shuts down, so I make the class implement IDisposable. 它需要在应用程序关闭时关闭流,因此我使类实现IDisposable。

That class is a member of another class, which is a member of another class etc. All the way up to my main app. 该类是另一个类的成员,它是另一个类的成员等。一直到我的主应用程序。

Do I therefore have to implement IDisposable on all of these classes? 因此,我是否必须在所有这些类上实现IDisposable?

What if I change my file implementation in the future so that it closes the file after each write? 如果我将来更改文件实现以便在每次写入后关闭文件怎么办? I now have a whole set of classes that implement IDisposable for no reason. 我现在有一整套类,无缘无故地实现了IDisposable。

I guess I'm uncomfortable with crowbarring IDisposable semantics into classes that have no need for them other than some slight implementation detail way down the chain. 我想我对将IDisposable语义扼杀到除了链上的一些轻微的实现细节之外不需要它们的类中感到不舒服。 Are there any ways around this? 这有什么办法吗?

In general if your type contains a member that implements IDisposable the type should also implement IDiposable as well. 通常,如果您的类型包含实现IDisposable的成员,则该类型也应该实现IDiposable It's the easiest way to enforce the IDisposable pattern. 这是强制执行IDisposable模式的最简单方法。

The one exception I use is if my types contract contains a method which 1) must be called and 2) signals the end of use for the IDisposable resource. 我使用的一个例外是我的类型契约包含一个方法,其中1)必须被调用,2)表示IDisposable资源的使用结束。 In that case I feel comfortable not implementing IDisposable and instead using that method to call Dispose 在这种情况下,我觉得没有实现IDisposable ,而是使用该方法调用Dispose

If you explicitly want to dispose the filestream, then yes, you need Implement IDisposable on any classes that hold a reference to your IDisposable. 如果你明确想要处理文件流,那么是的,你需要在任何持有IDisposable引用的类上实现IDisposable。 If it is reasonable to dispose the filestream after each write, ie does not hurt performance due to frequent wries, that sounds preferrable. 如果在每次写入之后处理文件流是合理的,即不会因频繁的吵闹而损害性能,那听起来更合适。

It depends on how you implement the class that uses the filestream. 这取决于您如何实现使用文件流的类。 If that class creates the filestream, then it should be responsible for disposing of it. 如果该类创建了文件流,那么它应该负责处理它。 However, if you were to change it so the method took in a filestream as a parameter, it would no longer 'own' the filestream and therefore not be responsible for disposing of it. 但是,如果您要更改它以便该方法将文件流作为参数,它将不再“拥有”文件流,因此不负责处理它。

If the class is part of some kind of hierarchy, you can just add a filestream as a parameter starting at the top and introduce it to all methods down to where it is actually used. 如果类是某种层次结构的一部分,则可以将文件流作为参数从顶部开始添加,并将其引入所有方法,直到实际使用的位置。

For example: 例如:

public class Class1
{
    private readonly Class2 SomeObject = new Class2();

    public void DoWork1(Filestream stream)
    {
        SomeObject.DoWork2(stream);
    }
}

public class Class2
{
    public void DoWork2(Filestream stream)
    {
        // Do the work required with the Filestream object
    }
}

While I'm not sure I'd use this pattern myself, this will allow you to not have to add 'IDisposable' to any classes except for the one that originally created the Filestream object. 虽然我不确定自己是否会使用此模式,但这样您就不必将“IDisposable”添加到除最初创建Filestream对象的类之外的任何类中。

You need an implementation of IDisposable in each, but that doesn't necessarily require an explicit implementation in the code of each. 您需要在每个中实现IDisposable ,但这并不一定需要在每个代码中使用显式实现。 Let inheritance do the work for you. 让继承为你工作。

Two approaches: 两种方法:

class FileHandlingClass : IDisposable
{
  private FileStream _stm;
  /* Stuff to make class interesting */
  public void Disposable()
  {
    _stm.Dispose();
  }
  /*Note that we don't need a finaliser btw*/
}

class TextHandlingClass : FileHandlingClass
{
  /* Stuff to make class interesting */
}

Now we can do: 现在我们可以做到:

using(TextHandlingClass thc = new TextHandlingClass())
  thc.DoStuff();

etc. 等等

This all works because TextHandlingClass inherits the only implementation of IDisposable it'll ever need. 这一切都有效,因为TextHandlingClass继承了它所需要的唯一IDisposable实现。

It gets tricker if we have further needs for disposing though: 如果我们有进一步的处理需求,它会变得棘手:

Say we've a class that handles pooling XmlNameTable objects (why that's a good idea is for another thread) and disposing it returns the table to the pool, and it's used by XmlHandlingClass . 假设我们有一个处理池化XmlNameTable对象的类(为什么这是另一个线程的好主意)并且处理它会将表返回到池中,并且它由XmlHandlingClass Now, we can deal with that to some extent with: 现在,我们可以在一定程度上解决这个问题:

class XmlHandlingClass : FileHandlingClass, IDisposable
{
  PooledNameTable _table;
  /* yadda */
  public new void Dispose() // another possibility is explicit IDisposable.Dispose()
  {
    _table.Dispose();
    base.Dispose();
  }
}

Now, this works great with: 现在,这适用于:

using(XmlHandlingClass x = new XmlHandlingClass())
  x.Blah();

but not with: 但不是:

using(FileHandlingClass x = new XmlHandlingClass())
  x.Blah()

In the latter case only the FileHandlingClass implementation is used (luckily not returning a pooled name-table to a pool is a minor matter, most cases of Dispose() are much more crucial). 在后一种情况下,仅使用FileHandlingClass实现(幸运的是,不将池化的名称表返回到池是一个小问题,大多数情况下Dispose()更为重要)。 Hence if needing overrides is possible, we should do: 因此,如果需要覆盖是可能的,我们应该这样做:

//Allow for Disposal override
class FileHandlingClass : IDisposable
{
  private FileStream _stm;
  /* Stuff to make class interesting */
  public virtual void Disposable()
  {
    _stm.Dispose();
  }
  /*Note that we don't need a finaliser btw*/
}

//Still don't care here
class TextHandlingClass : FileHandlingClass
{
  /* Stuff to make class interesting */
}

class XmlHandlingClass : FileHandlingClass
{
  PooledNameTable _table;
  /* yadda */
  public override void Dispose()
  {
    _table.Dispose();
    base.Dispose();
  }
}

Now we have much more safety in the calls to Dispose() , but still only need to implement it ourselves where it matters. 现在我们对Dispose()的调用有了更多的安全性,但仍然只需要在重要的地方自己实现它。

There's a minute performance hit in the second case, but it really is minute. 在第二种情况下有一分钟的性能影响,但它确实是微小的。 I'm pointing it out now only to argue against considering the first in any case where needing to override Dispose() is seen as even vaguely plausible. 我现在指出它只是为了反对在任何情况下考虑第一个需要覆盖Dispose()被认为甚至模糊似乎合理。

At any given moment, every instance of every type which implements IDisposable should have at least one (and usually exactly one) entity which can be expected to call Dispose on it sometime after it is no longer needed, and before it is completely and ultimately abandoned. 在任何给定时刻,实现IDisposable的每个类型的每个实例都应该至少有一个(通常只有一个)实体,可以预期在不再需要它之后的某个时间调用Dispose,并且在它完全被最终放弃之前。 If your type has a field of type IDisposable, but something else can be expected to dispose of any IDisposable instance it might refer to, then you should not call Dispose on that field yourself. 如果你的类型有一个IDisposable类型的字段,但是其他东西可能会被处理掉它可能引用的任何IDisposable实例,那么你不应该自己调用该字段上的Dispose。 If your type has an IDisposable field, nobody else is going to use that object once you're done with it, and nobody else is expected to Dispose it, then you should call Dispose on the object once you no longer need it. 如果你的类型有一个IDisposable字段,那么一旦你完成它就没有其他人会使用那个对象,并且没有其他人需要Dispose它,那么你应该在对象上调用Dispose一旦你不再需要它。 In many cases, your object will need the other object until such time as no other object needs yours, and the way your object will find that out is when someone else calls Dispose on it (whereupon it will call Dispose on the other objects). 在许多情况下,您的对象将需要另一个对象,直到没有其他对象需要您的对象,并且您的对象将找到它的方式是当其他人在其上调用Dispose时(此时它将在其他对象上调用Dispose)。

One pattern which can sometimes be useful is to have a class expose a Disposed event which gets raised whenever Dispose is called. 一种有时可能有用的模式是让一个类公开一个Disposed事件,只要调用Dispose就会引发该事件。 This can be useful if, eg, an another object gives your object a reference to IDisposable which it will need for awhile, and then the object that gave you the IDisposable gets done with it. 这可能很有用,例如,如果另一个对象为您的对象提供IDisposable的引用,它将需要一段时间,然后给您IDisposable的对象完成它。 It can't dispose the object while your object still needs it, and your object isn't going to dispose it (since your object won't know whether the object that supplied the IDisposable is done with it). 当对象仍然需要它时,它不能处理对象,并且你的对象不会处理它(因为你的对象不知道提供IDisposable的对象是否完成了它)。 If the class which gave your class the IDisposable hooks to your object's Disposed handler, however, the event handler can then note that your object no longer needs the IDisposable and either Dispose it immediately (if your object was the last one to need it) or set a flag so that when the other user is finished with the object it will get Disposed). 但是,如果为您的类提供了IDisposable挂钩到您的对象的Disposed处理程序的类,那么事件处理程序可以注意到您的对象不再需要IDisposable并立即处置它(如果您的对象是最后一个需要它)或者设置一个标志,以便当其他用户完成对象时,它将获得Disposed)。

Another pattern which can be useful if your object will have a certain set of disposable objects that it will keep throughout its lifetime is to keep a List of IDisposable objects and then have your Dispose method iterate through the list and dispose things therein. 如果你的对象在其整个生命周期中将保留一定数量的一次性对象,那么另一种模式可能很有用:保留一个IDisposable对象列表,然后让你的Dispose方法遍历列表并在其中处理。 Each item on the list should be Disposed within its own Try/Catch block; 列表中的每个项目都应在其自己的Try / Catch块中处置; if an exception occurs, throw a CleanupFailureException (a custom type) which has either the first or last such exception as its InnerException, and also includes a list of all the exceptions that occurred as a custom property. 如果发生异常,则抛出一个CleanupFailureException(一个自定义类型),它具有与InnerException相关的第一个或最后一个此类异常,并且还包括作为自定义属性发生的所有异常的列表。

As a rule of thumb, when a type keeps a reference to an IDisposable in an instance field, I make it disposable too. 根据经验,当一个类型在实例字段中保留对IDisposable的引用时,我也将其设置为一次性的。 But I usually try to avoid finding myself in this situation; 但我通常会尽量避免在这种情况下找到自己; when possible, I try to dispose disposables in the same method where they were created, with a using block. 在可能的情况下,我尝试using块以与创建它们相同的方法处置一次性物品。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM