为什么已处置的对象在处置后使用它时不会抛出异常?
在已处置对象上调用方法是否合法?如果是,为什么?
在下面的演示程序中,我有一个一次性类 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()");
a.f();
using ( a)
{
Console.WriteLine("Inside using()");
a.f();
}
Console.WriteLine("After using()");
a.f();
}
}
Output (ideone):
Before using()
100
Inside using()
200
Dispose() called
After using()
400
如何在已处置的对象 a
上调用 f()
?这是允许的吗?如果是,那为什么?如果不是,那么为什么上面的程序在运行时没有给出异常?
我知道使用 using
的流行结构是这样的:
using (A a = new A())
{
//working with a
}
但我只是在尝试,这就是为什么我以不同的方式编写它。
Is it legal to call a method on disposed object? If yes, why?
In the following demo program, I've a disposable class A
(which implements IDisposable
interface).As far as I know, if I pass disposable object to using()
construct, then Dispose()
method gets called automatically at the closing bracket:
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.
If that is correct, then please explain the output of this program:
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()");
a.f();
using ( a)
{
Console.WriteLine("Inside using()");
a.f();
}
Console.WriteLine("After using()");
a.f();
}
}
Output (ideone):
Before using()
100
Inside using()
200
Dispose() called
After using()
400
How can I call f()
on the disposed object a
? Is this allowed? If yes, then why? If no, then why the above program doesn't give exception at runtime?
I know that the popular construct of using using
is this:
using (A a = new A())
{
//working with a
}
But I'm just experimenting, that is why I wrote it differently.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
处置并不意味着消失。已处置仅意味着任何非托管资源(例如文件、任何类型的连接......)已被释放。虽然这通常意味着该对象不提供任何有用的功能,但仍然可能有一些方法不依赖于该非托管资源并且仍然照常工作。
处置机制的存在是因为 .net(继承自 C#.net)是一个垃圾收集环境,这意味着您不负责内存管理。但是,垃圾收集器无法确定非托管资源是否已使用完毕,因此您需要自己执行此操作。
如果您希望方法在对象被处置后抛出异常,则需要一个布尔值来捕获处置状态,一旦对象被处置,您就抛出异常:
Disposed doesn't mean gone. Disposed only means that any unmanaged resource (like a file, connection of any kind, ...) has been released. While this usually means that the object doesn't provide any useful functionality, there might still be methods that don't depend on that unmanaged resource and still work as usual.
The Disposing mechanism exist as .net (and inheritly, C#.net) is a garbage-collected environment, meaning you aren't responsable for memory management. However, the garbage collector can't decide if an unmanaged resource has been finished using, thus you need to do this yourself.
If you want methods to throw an exception after the object has been diposed, you'll need a boolean to capture the dispose status, and once the object is disposed, you throw the exception:
不会引发异常,因为您没有设计在调用
Dispose
后引发ObjectDisposeException
的方法。clr 不会自动知道在调用 Dispose 后它应该抛出
ObjectDisposeException
。如果 Dispose 已释放成功执行方法所需的任何资源,则您有责任抛出异常。The exception is not thrown because you have not designed the methods to throw
ObjectDisposedException
afterDispose
has been called.The clr does not automagically know that it should throw
ObjectDisposedException
once Dispose is called. It's your responsibility to throw an exception if Dispose has released any resources needed for successful execution of your methods.典型的 Dispose() 实现仅对存储在可一次性字段中的任何对象调用 Dispose()。进而释放非托管资源。如果您实现 IDisposable 但实际上没有执行任何操作(就像您在代码片段中所做的那样),则对象状态根本不会改变。一切都不会出错。不要将处置与最终确定混淆。
A typical Dispose() implementation only calls Dispose() on any objects that it stores in its fields that are disposable. Which in turn release unmanaged resources. If you implement IDisposable and not actually do anything, like you did in your snippet, then the object state doesn't change at all. Nothing can go wrong. Don't mix up disposal with finalization.
IDisposable 的目的是允许对象修复任何外部实体的状态,这些外部实体为了自身的利益而被置于出于其他目的而不太理想的状态。例如,Io.Ports.SerialPort 对象可能已将串行端口的状态从“可用于任何需要它的应用程序”更改为“仅可由一个特定的 Io.Ports.SerialPort 对象使用”; SerialPort.Dispose 的主要目的是将串行端口的状态恢复为“可用于任何应用程序”。
当然,一旦实现 IDisposable 的对象重置了为其利益而维护某种状态的实体,它将不再享有这些实体所维护状态的好处。例如,一旦串行端口的状态被设置为“可用于任何应用程序”,与其关联的数据流就不能再用于发送和接收数据。如果一个对象可以正常运行,而无需外部实体为其利益而进入特殊状态,那么首先就没有理由让外部实体处于特殊状态。
通常,在对象上调用 IDisposable.Dispose 后,不应期望该对象能够执行太多操作。尝试在此类对象上使用大多数方法都将表明存在错误;如果无法合理地预期某个方法可以工作,则表明该方法的正确方法是通过 ObjectDisposeException。
Microsoft 建议,如果在已释放的对象上使用实现 IDisposable 的对象上的几乎所有方法,则应抛出 ObjectDisposeException。我认为这样的建议过于宽泛。对于设备来说,公开方法或属性以了解对象存活时发生的情况通常非常有用。虽然可以为通信类提供一个 Close 方法和一个 Dispose 方法,并且只允许在 close 后查询 NumberOfPacketsExchanged 之类的内容,但不允许在 Dispose 后查询,但这似乎过于复杂。读取与对象被处置之前发生的事情相关的属性似乎是一种完全合理的模式。
The purpose of IDisposable is to allow an object to fix the state of any outside entities which have, for its benefit, been put into a state that is less than ideal for other purposes. For example, an Io.Ports.SerialPort object might have changed the state of a serial port from "available for any application that wants it" to "only usable by one particular Io.Ports.SerialPort object"; the primary purpose of SerialPort.Dispose is to restore the state of the serial port to "available for any application".
Of course, once an object that implements IDisposable has reset entities that had been maintaining a certain state for its benefit, it will no longer have the benefit of those entities' maintained state. For example, once the state of the serial port has been set to "available for any application", the data streams with which it had been associated can no longer be used to send and receive data. If an object could function normally without outside entities being put into a special state for its benefit, there would be no reason to leave outside entities in a special state in the first place.
Generally, after IDisposable.Dispose has been called on an object, the object should not be expected to be capable of doing much. Attempting to use most methods on such an object would indicate a bug; if a method can't reasonably be expected to work, the proper way to indicate that is via ObjectDisposedException.
Microsoft suggests that nearly all methods on an object which implements IDisposable should throw ObjectDisposedException if they are used on an object which has been disposed. I would suggest that such advice is overbroad. It is often very useful for devices to expose methods or properties to find out what happened while the object was alive. Although one could give a communications class a Close method as well as a Dispose method, and only allow one to query things like NumberOfPacketsExchanged after a close but not after a Dispose, but that seems excessively complicated. Reading properties related to things that happened before an object was Disposed seems a perfectly reasonable pattern.
调用
Dispose()
不会将对象引用设置为null
,并且您的自定义一次性类不包含任何在 < 之后访问其函数时引发异常的逻辑。 code>Dispose() 已经被调用,所以它当然是合法的。在现实世界中,
Dispose()
会释放非托管资源,并且这些资源此后将不可用,并且/或者如果您尝试使用该对象,类作者会抛出ObjectDisposeException
调用Dispose()
后。通常,类级别的布尔值会在Dispose()
的主体内设置为 true,并且在类的其他成员执行任何操作之前检查该值,如果布尔值会抛出异常,是真的。Calling
Dispose()
doesn't set the object reference tonull
, and your custom disposable class doesn't contain any logic to throw an exception if its functions are accessed afterDispose()
has been called so it is of course legal.In the real world,
Dispose()
releases unmanaged resources and those resources will be unavailable thereafter, and/or the class author has it throwObjectDisposedException
if you try to use the object after callingDispose()
. Typically a class-level boolean would be set to true within the body ofDispose()
and that value checked in the other members of the class before they do any work, with the exception being thrown if the bool is true.C# 中的处置器与 C++ 中的析构函数不同。处置器用于在对象保持有效的同时释放托管(或非托管)资源。
异常的抛出取决于类的实现。如果
f()
不需要使用已经释放的对象,那么它不一定需要抛出异常。A disposer in C# is not the same as a destructor in C++. A disposer is used to release managed (or unmanaged) resources while the object remains valid.
Exceptions are thrown depending on the implementation of the class. If
f()
does not require the use of your already disposed objects, then it doesn't necessarily need to throw an exception.