为什么处置对象在处理后使用它时不会抛出exception?

在被处置对象上调用方法是否合法? 如果是,为什么?

在下面的演示程序中,我有一个一次性类A (它实现了IDisposable接口)。据我所知,如果我将一次性对象传递给using()构造,那么Dispose()方法会在结束括号中自动调用:

 A a = new A(); using (a) { //... }//<--------- a.Dispose() gets called here! //here the object is supposed to be disposed, //and shouldn't be used, as far as I understand. 

如果这是正确的,那么请解释这个程序的输出:

 public class A : IDisposable { int i = 100; public void Dispose() { Console.WriteLine("Dispose() called"); } public void f() { Console.WriteLine("{0}", i); i *= 2; } } public class Test { public static void Main() { A a = new A(); Console.WriteLine("Before using()"); af(); using ( a) { Console.WriteLine("Inside using()"); af(); } Console.WriteLine("After using()"); af(); } } 

输出( ideone ):

 Before using() 100 Inside using() 200 Dispose() called After using() 400 

如何调用已处置对象上的f() ? 这是允许的吗? 如果是,那么为什么呢? 如果不是,那么为什么上述程序在运行时没有给出exception?


我知道使用的流行结构是这样的:

 using (A a = new A()) { //working with a } 

但我只是在尝试,这就是我写不同的原因。

处置并不意味着消失。 仅处理意味着已释放任何非托管资源(如文件,任何类型的连接……)。 虽然这通常意味着该对象不提供任何有用的function,但仍可能存在不依赖于该非托管资源且仍然照常工作的方法。

处置机制存在为.net(并且inheritance,C#.net)是一个垃圾收集环境,这意味着您不负责内存管理。 但是,垃圾收集器无法确定是否已完成非托管资源的使用,因此您需要自己执行此操作。

如果希望方法在对象被放置后抛出exception,则需要一个布尔值来捕获dispose状态,一旦处理了对象,就抛出exception:

 public class A : IDisposable { int i = 100; bool disposed = false; public void Dispose() { disposed = true; Console.WriteLine("Dispose() called"); } public void f() { if(disposed) throw new ObjectDisposedException(); Console.WriteLine("{0}", i); i *= 2; } } 

不抛出exception,因为在调用Dispose之后,您没有设计抛出ObjectDisposedException的方法。

一旦调用Dispose,clr就不会自动知道它应该抛出ObjectDisposedException 。 如果Dispose已经释放了成功执行方法所需的任何资源,那么您有责任抛出exception。

典型的Dispose()实现仅在它存储在一次性字段中的任何对象上调用Dispose()。 这又反过来释放非托管资源。 如果您实现IDisposable并且实际上没有做任何事情,就像您在代码段中所做的那样,那么对象状态根本不会改变。 什么都不会出错。 不要混淆处理和最终确定。

IDisposable的目的是允许一个对象修复任何外部实体的状态,这些外部实体为了它的利益而被置于一个不太理想的状态用于其他目的。 例如,Io.Ports.SerialPort对象可能已将串行端口的状态从“可用于任何需要它的应用程序”更改为“仅可由一个特定的Io.Ports.SerialPort对象使用”; SerialPort.Dispose的主要目的是将串行端口的状态恢复为“可用于任何应用程序”。

当然,一旦实现IDisposable的对象重置了为了其利益而维持某个状态的实体,它将不再具有这些实体维护状态的好处。 例如,一旦串行端口的状态设置为“可用于任何应用程序”,与其关联的数据流就不能再用于发送和接收数据。 如果一个对象可以正常运行而外部实体没有为了它的利益而被置于特殊状态,那么就没有理由首先将外部实体留在特殊状态。

通常,在对象上调用IDisposable.Dispose之后,不应期望该对象能够做很多事情。 尝试在这样的对象上使用大多数方法会表明存在错误; 如果一个方法无法合理地预期工作,则表明这是通过ObjectDisposedException的正确方法。

Microsoft建议,实现IDisposable的对象上的几乎所有方法都应抛出ObjectDisposedException,如果它们用于已经处置的对象上。 我建议这样的建议过于宽泛。 设备通常非常有用,可以公开方法或属性,以找出对象存活时发生的情况。 虽然可以给一个通信类一个Close方法以及一个Dispose方法,但只允许一个人在关闭后查询像NumberOfPacketsExchanged这样的东西,但是在Dispose之后,但这看起来过于复杂。 读取与在Disposed之前发生的事情相关的属性似乎是一个完全合理的模式。

C#中的处理程序与C ++中的析构函数不同。 处理程序用于在对象保持有效时释放托管(或非托管)资源。

根据类的实现,抛出exception。 如果f()不需要使用已经处理过的对象,那么它不一定需要抛出exception。

调用Dispose()不会将对象引用设置为null ,并且如果在调用Dispose()之后访问其函数,则自定义的一次性类不包含任何抛出exception的逻辑,因此它当然是合法的。

在现实世界中, Dispose()释放非托管资源,此后这些资源将不可用,和/或如果在调用Dispose()后尝试使用该对象,则类作者会抛出ObjectDisposedException 。 通常,在Dispose()体内将类级布尔值设置为true,并且在执行任何工作之前在类的其他成员中检查该值,如果bool为true则抛出exception。