简体   繁体   English

在没有资源的类上实现IDisposable有什么好处吗?

[英]Is there any benefit to implementing IDisposable on classes which do not have resources?

In C#, if a class, such as a manager class, does not have resources, is there any benefit to having it : IDisposable ? 在C#中,如果某个类(如经理类)没有资源,那么拥有它是否有任何好处: IDisposable

Simple example: 简单的例子:

public interface IBoxManager
{
 int addBox(Box b);
}

public class BoxManager : IBoxManager
{
 public int addBox(Box b)
 {
  using(dataContext db = new dataContext()){
   db.Boxes.add(b);
   db.SaveChanges();
  }
  return b.id;
 }
}

Will there be any benefit in memory use when using BoxManager if it also implements IDisposable? 使用BoxManager时,如果它还实现了IDisposable,那么内存使用会有什么好处吗? public class BoxManager : IBoxManager , IDisposable

For example: 例如:

BoxManager bm = new BoxManager();
bm.add(myBox);
bm.dispose();//is there benefit to doing this?

There are only 2 reasons for implementing IDisposable on a type 在类型上实现IDisposable只有两个原因

  • The type contains native resources which must be freed when the type is no longer used 该类型包含必须在不再使用该类型时释放的本机资源
  • The type contains fields of type IDisposable 该类型包含IDisposable类型的字段

If neither of these are true then don't implement IDisposable 如果这些都不是真的那么就不要实现IDisposable

EDIT 编辑

Several people have mentioned that IDisposable is a nice way to implement begin / end or bookended operations. 有几个人提到IDisposable是实现开始/结束或预订操作的好方法。 While that's not the original intent of IDisposable it does provide for a very nice pattern. 虽然这不是IDisposable的原始意图,但确实提供了一个非常好的模式。

class Operation {
  class Helper : IDisposable {
    internal Operation Operation;
    public void Dispose() {
      Operation.EndOperation();
    }
  }
  public IDisposable BeginOperation() {
    ...
    return new Helper() { Operation = this };
  }
  private void EndOperation() {
    ...
  }
}

Note: Another interesting way to implement this pattern is with lambdas. 注意:实现此模式的另一种有趣方式是使用lambdas。 Instead of giving an IDisposable back to the user and hoping they don't forget to call Dispose have them give you a lambda in which they can execute the operation and you close out the operation 而不是给用户一个IDisposable并希望他们不要忘记调用Dispose让他们给你一个lambda,他们可以在其中执行操作并关闭操作

public void BeginOperation(Action action) {
  BeginOperationCore();
  try {
    action();
  } finally {
    EndOperation();
  }
}

如果您没有明确地使用Dispose()方法,则一次性和非一次性版本之间不会有任何差异。

While your code wouldn't benefit from implementing IDisposable, I can't agree with other opinions here that state that IDisposable is only meant to (directly or indirectly) free native resources. 虽然您的代码不会从实现IDisposable中受益,但我不同意其他意见,其中声明IDisposable仅用于(直接或间接)释放本机资源。 IDisposable can be used whenever the object needs to perform clean up task at the end of it's lifetime span. 只要对象需要在其生命周期结束时执行清理任务,就可以使用IDisposable。 It's extremely useful together with using . 它与using一起非常有用。

A very popular example: in ASP.NET MVC Html.BeginForm returns an IDisposable. 一个非常流行的例子:在ASP.NET MVC中, Html.BeginForm返回一个IDisposable。 On creation, the object opens the tag, when Dispose is called it closes it. 在创建时,对象打开标记,当调用Dispose时它会关闭它。 No native resources involved, yet still a good use of IDisposable. 没有涉及本地资源,但仍然很好地使用IDisposable。

不,如果您没有做一些有用的事情,例如释放您的类可能在Dispose方法中保存的非托管资源,那么将没有任何好处。

No, if there are no (managed or unmanaged) resources there is no need for IDisposable either. 不,如果没有(托管或非托管)资源,也不需要IDisposable

Small caveat: some people use IDisposable to clean up eventhandlers or large memory buffers but 小警告:有些人使用IDisposable来清理事件处理程序或大内存缓冲区但是

  • you don't seem to use those 你似乎没有使用那些
  • it's a questionable pattern anyway. 无论如何,这是一个有问题的模式。

One major point of confusion, which may not be applicable in your case but arises often, is what exactly constitutes a "resource". 一个主要的混淆点,可能不适用于您的情况,但经常出现,究竟是什么构成了“资源”。 From the perspective of an object, an unmanaged resource is something which an outside entity ( ) is "doing"( *) on its behalf, which that outside entity will keep doing--to the detriment of other entitites--until told to stop. 从一个对象的角度来看,非托管资源是外部实体( 代表它“做”( *)的东西,外部实体将继续这样做 - 不利于其他权利 - 直到被告知要停止。 For example, if an object opens a file, the machine which hosts the file may grant that object exclusive access, denying everyone else in the universe a chance to use it unless or until it gets notified that the exclusive access isn't needed anymore. 例如,如果一个对象打开一个文件,那么托管该文件的机器可以授予该对象独占访问权,拒绝宇宙中的其他人有机会使用它,除非或者直到它被告知不再需要独占访问。

(*) which could be anything, anywhere; (*)可以是任何地方,任何地方; possibly not even on the same computer. 可能甚至没有在同一台计算机上。

(**) or some way in which the the behavior or state of an outside entity is altered (**)或改变外部实体的行为或状态的某种方式

If an outside entity is doing something on behalf of an object which is abandoned and disappears without first letting the entity know its services are no longer required, the outside entity will have no way of knowing that it should stop acting on behalf of the object which no longer exists. 如果外部实体代表被抛弃的对象做某事并且在没有首先让实体知道其服务不再需要的情况下消失,那么外部实体将无法知道它应该停止代表对象的行为。不复存在。 IDisposable provides one way of avoiding this problem by providing a standard means of notifying objects when their services are not required. IDisposable通过提供在不需要其服务时通知对象的标准方法,提供了一种避免此问题的方法。 An object whose services are no longer required will generally not need to ask any further favors from any other entities, and will thus be able to request that any entities that had been acting on its behalf should stop doing so. 不再需要其服务的对象通常不需要向任何其他实体询问任何进一步的好处,因此能够请求代表其行事的任何实体应该停止这样做。

To allow for the case where an object gets abandoned without IDisposable.Dispose() having been called first, the system allows objects to register a "failsafe" cleanup method called Finalize() . 为了允许在没有首先调用IDisposable.Dispose()的情况下放弃对象的情况,系统允许对象注册名为Finalize()的“故障安全”清理方法。 Because for whatever reason, the creators of C# don't like the term Finalize() , the language requires the use of a construct called a "destructor" which does much the same thing. 因为无论出于何种原因,C#的创建者不喜欢Finalize()这个术语,该语言需要使用一个名为“析构函数”的构造,它做了很多相同的事情。 Note that in general, Finalize() will mask rather than solve problems, and can create problems of its own, so it should be used with extreme caution if at all. 请注意,一般来说, Finalize()将掩盖而不是解决问题,并且可能产生自己的问题,因此如果有的话应该非常谨慎地使用它。

A "managed resource" is typically a name given to an object which implements IDisposable and usually, though not always, implements a finalizer. “托管资源”通常是赋予实现IDisposable的对象的名称,通常但不总是实现终结器。

From my personal experience (confirmed with discussion and other posts here) I would say, that there could be a situations where your object use massive amount of events, or not massive amount but frequent subscriptions and unsubscription from the event which sometimes leads to that the object is not garbage collected. 根据我的个人经验(通过讨论和其他帖子确认),我会说,可能存在这样的情况:您的对象使用大量事件,或者没有大量但经常订阅和取消订阅事件有时会导致对象不是垃圾回收。 In this case I in Dispose unsubscribe from all events my object subscribed before. 在这种情况下, Dispose取消订阅我之前订阅的所有事件。

Hope this helps. 希望这可以帮助。

IDisposable is also great if you want to benefit the using () {} syntax. 如果您想要using () {}语法, IDisposable也很棒。

In a WPF project with ViewModels, I wanted to be able to temporarily disable NotifyPropertyChange events from raising. 在使用ViewModels的WPF项目中,我希望能够暂时禁用NotifyPropertyChange事件。 To be sure other developers will re-enable notifications, I wrote a bit of code to be able to write something like: 为了确保其他开发人员将重新启用通知,我写了一些代码,以便能够编写如下内容:

using (this.PreventNotifyPropertyChanges()) {
    // in this block NotifyPropertyChanged won't be called when changing a property value
}

The syntax looks okay and is easily readable. 语法看起来不错,易于阅读。 For it to work, there's a bit of code to write. 要使它工作,需要编写一些代码。 You will need a simple Disposable object and counter. 您将需要一个简单的Disposable对象和计数器。

public class MyViewModel {
    private volatile int notifyPropertylocks = 0; // number of locks

    protected void NotifyPropertyChanged(string propertyName) {
        if (this.notifyPropertylocks == 0) { // check the counter
            this.NotifyPropertyChanged(...);
        }
    }

    protected IDisposable PreventNotifyPropertyChanges() {
        return new PropertyChangeLock(this);
    }

    public class PropertyChangeLock : IDisposable {
        MyViewModel vm;

        // creating this object will increment the lock counter
        public PropertyChangeLock(MyViewModel vm) {
            this.vm = vm;
            this.vm.notifyPropertylocks += 1;
        }

        // disposing this object will decrement the lock counter
        public void Dispose() {
            if (this.vm != null) {
                this.vm.notifyPropertylocks -= 1;
                this.vm = null;
            }
        }
    }
}

There are no resources to dispose here. 这里没有资源可供处理。 I wanted a clean code with a kind of try/finally syntax. 我想要一个带有一种try / finally语法的干净代码。 The using keyword looks better. using关键字看起来更好。

is there any benefit to having it : IDisposable? 拥有它有什么好处:IDisposable?

It doesn't look so in your specific example, however : there is one good reason to implement IDisposable even if you don't have any IDisposable fields: your descendants might. 它不会在你的具体的例子来看的话, 但是 :有实现一个很好的理由IDisposable ,即使你没有任何IDisposable字段:你的子孙可能。

This is one of the big architectural problems of IDisposable highlighted in IDisposable: What your mother never told you about resource deallocation . 这是IDisposable强调的IDisposable的一个重大架构问题:你母亲从未告诉过你有关资源释放的问题 Basically, unless your class is sealed you need to decide whether your descendants are likely to have IDisposable members. 基本上,除非您的课程被密封,否则您需要决定您的后代是否可能有IDisposable成员。 And this isn't something you can realistically predict. 这不是你能够实际预测的东西。

So, if your descendants are likely to have IDisposable members, that might be a good reason to implement IDisposable in the base class. 因此,如果您的后代可能具有IDisposable成员,那么这可能是在基类中实现IDisposable一个很好的理由。

Short answer would be no. 简短的回答是没有。 However, you can smartly use the nature of the Dispose() executement at the end of the object lifecycle. 但是,您可以巧妙地在对象生命周期结束时使用Dispose()执行的本质。 One have already gave a nice MVC example ( Html.BeginForm ) 一个人已经给出了一个很好的MVC示例( Html.BeginForm

I would like to point out one important aspect of IDisposable and using() {} statement. 我想指出IDisposableusing() {}语句的一个重要方面。 At the end of the Using statement Dispose() method is automatically called on the using context object (it must have implemented IDisposable interface, of course). Using语句结束时,在Using上下文对象上自动调用Dispose()方法(当然,它必须已经实现了IDisposable接口)。

There one more reason that no one mentioned (though it's debateful if it really worth it): The convension says that if someone uses a class that implement IDisposable , it must call its Dispose method (either explicitly or via the 'using' statement). 还有一个原因是没有人提到过(虽然它确实是值得的,但它仍然是有争议的):对象说如果有人使用实现IDisposable的类,它必须调用它的Dispose方法(显式或通过'using'语句)。 But what happens if V1 of a class (in your public API) didn't need IDisposable, but V2 does? 但是如果一个类的V1(在你的公共API中)不需要IDisposable会发生什么呢,但V2呢? Technically it doesn't break backward compatibility to add an interface to a class, but because of that convension , it is! 从技术上讲,它不会破坏向后兼容性添加接口到类,但由于这种对流 ,它是! Because old clients of your code won't call its Dispose method and may cause resources to not get freed. 因为代码的旧客户端不会调用其Dispose方法,并且可能导致资源无法释放。 The (almost) only way to avoid it is to implement IDisposable in any case you suspect that you'll need it in the future, to make sure that your clients always call your Dispose method, that some day may be really needed. (几乎)避免它的唯一方法是在任何情况下实施IDisposable,你怀疑将来需要它,以确保你的客户总是调用你的Dispose方法,有一天可能真的需要它。 The other (and probably better) way is to implemet the lambda pattern mentioned by JaredPar above in the V2 of the API. 另一种(可能更好的)方法是在API的V2中实现上面JaredPar提到的lambda模式。

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

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