C# 一次性问题

发布于 2024-11-04 19:49:47 字数 230 浏览 5 评论 0原文

例如,如果我忘记编写 using ,垃圾收集器是否会自动释放与某些 IDisposable 实例关联的非托管资源(实际上)陈述?

显然,我不知道什么时候会发生,但是当我不在乎时将IDisposable留给GC可以吗?关于这些资源,我同意它们最终会被处置吗?

Would the garbage collector automatically free the unmanaged resources (whatever, actually) associated with some IDisposable instance if, for example, I forgot to write using statement?

Obviously, I don't know when this would happen, but is it fine to leave the IDisposable to the GC when I don't care about those resources and I'm fine with that they will be disposed eventually?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

抚你发端 2024-11-11 19:49:47

例如,如果我忘记编写 using 语句,垃圾收集器是否会自动释放与某些 IDisposable 实例关联的非托管资源(实际上)?

通常,但不一定。一次性资源的作者需要做正确的事情。

显然,我不知道什么时候会发生这种情况,但是当我不关心这些资源并且我同意它们最终会被处置时,将 IDisposable 留给 GC 可以吗?

你的问题预设了一个谎言。 垃圾收集器永远不会调用 Dispose。相反,垃圾收集器会调用析构函数(如果您愿意,也可以称为“终结器”)。对于健忘的用户来说,析构函数可能会也可能不会调用 Dispose。

你的问题也说明态度不好。 可能不关心资源是否被延迟释放,但另一个程序肯定可能会关心!如果您的客户运行两个程序,并且都尝试访问相同的非托管资源,一个是您编写的,另一个是其他人编写的,该怎么办? 做一个好公民;用完后立即释放你的稀缺资源,以便其他程序可以使用它们。这就是“使用”的真正目的——为了礼貌,确保稀缺资源被快速回收。

Dispose 模式的正确实现将确保析构函数在用户忘记调用 Dispose 时清理非托管资源,并确保析构函数在用户记得时不会清理资源。

如果您是为拥有非托管资源的类编写 Dispose 实现的人您有责任为用户未正确调用 Dispose 的情况提供正确的析构函数代码。正确的方法编写此代码的文档非常详细;遵循模式。请参阅乔恩的评论以获取一些有用的链接。

另请注意,如果您正在编写这样的类,那么您还需要使其在用户不可能调用 Dispose 的情况下工作。例如,假设构造函数分配两个非托管资源,并且在第一个和第二个资源的分配之间,会在构造函数外部引发并捕获异常。那么用户就无法调用 Dispose,因为新对象引用的分配发生在构造函数成功运行之后,而构造函数从未成功完成运行。第一个非托管资源是如何释放的?只有析构函数才能释放它。面对这种情况,析构函数必须具有鲁棒性;被破坏的对象可能永远不会被完全构造,因此您不能依赖构造函数的任何不变量。

Would the garbage collector automatically free the unmanaged resources (whatever, actually) associated with some IDisposable instance if, for example, I forgot to write using statement?

Usually, but not necessarily. The author of a disposable resource needs to do the right thing.

Obviously, I don't know when this would happen, but is it fine to leave the IDisposable to the GC when I don't care about those resources and I'm fine with that they will be disposed eventually?

Your question presupposes a falsehood. The garbage collector never calls Dispose, ever. Rather, the garbage collector calls the destructor (or "finalizer" if you prefer). It is the destructor which might or might not call Dispose for the forgetful user.

Your question also indicates a bad attitude. You might not care if the resources are freed late but another program certainly might care! What if your customer is running two programs that both try to access the same unmanaged resource, one written by you and one written by someone else? Be a good citizen; release your scarce resources as soon as you are done with them so that other programs can use them. That's the real purpose of "using" -- it's to be polite by ensuring that scarce resources are reclaimed quickly.

A correct implementation of the Dispose pattern will ensure that the destructor cleans up the unmanaged resources if the user forgets to call Dispose, and will ensure that the destructor does NOT clean up the resources if they remember.

If you are the person writing the implementation of Dispose for a class that owns unmanaged resources it is your responsibility to get the destructor code right for the case where the user does not call Dispose correctly. The correct way to write this code is extremely well documented; follow the pattern. See Jon's comments for some helpful links.

Note also that if you are writing such a class then you are required to also make it work in scenarios in which it is impossible for the user to call Dispose. For example, suppose the constructor allocates two unmanaged resources, and between the allocation of the first and second, an exception is thrown and caught outside of the constructor. There's then no way for the user to ever call Dispose because the assignment of the new object reference happens after the constructor runs successfully, and the constructor never finished running successfully. How is the first unmanaged resource freed? Only the destructor can free it. The destructor has to be robust in the face of such scenarios; the object destructed might never have been fully constructed so you cannot rely on any invariant of the constructor.

-残月青衣踏尘吟 2024-11-11 19:49:47

是的,它们最终会被清理掉。仅当您需要确定性资源清理时,例如 GDI、Sql 和其他基于系统句柄的代码,或者对于 DataTable 等可能占用大量内存的对象,才需要使用 using (呵呵..) 。

编辑:这是假设您正确实现了 Dispose 模式并让析构函数调用 Dispose。

Yes, they would be cleaned eventually. You only need to use using (heh..) when you need deterministic resource cleanup, like for GDI, Sql and other system handle-based code, or for potentially memory-heavy objects like DataTable.

Edit: This is assuming you implemented the Dispose pattern right and have your destructor call Dispose.

叫嚣ゝ 2024-11-11 19:49:47

如果对象通过在终结器中包含对 Dispose 方法的条件调用来正确实现 IDisposable 接口,则 GC 将在收集期间触发对象的处置(通过终结器)。

有些事情会导致这种情况不会发生,但如果遵循标准模式,那么它在大多数情况下都应该有效。

如果对象在终结器中不包含处理代码,那么所有的赌注都会被取消。在太阳超新星爆发之前,不受管理的资源可能一直未被处置。

If an object implements the IDisposable interface correctly, by including a conditional call to the Dispose method in the finalizer, then the GC will trigger disposal of the object during a collection (via the finalizer).

There are things that cause this not to happen, but if the standard pattern is followed, then it should work in most cases.

If the object does not include disposal code in the finalizer, then all bets are off. The unmanaged resources may remain undisposed until the sun supernovas.

尾戒 2024-11-11 19:49:47

不,不一定。这取决于您如何获得非托管资源。

如果您有非托管资源的直接句柄(可能是IntPtr),那么您应该使用终结器或使用SafeHandle。否则你肯定可能泄漏非托管资源。

如果您的类型仅引用诸如 FileStream 之类的内容,那么您应该期望该类型将具有终结器。

现在,很少需要从您的类型中直接访问非托管资源,并且如果您使用框架类型,它们确实应该进行适当的清理(但不确定)。不过,这是可能的。

No, not necessarily. It depends on how you've got the unmanaged resources.

If you've got a direct handle to the unmanaged resources (probably an IntPtr) then you should either have a finalizer or use SafeHandle... otherwise you definitely could leak unmanaged resources.

If your type just has a reference to something like a FileStream, then you should expect that that type will have a finalizer.

Now it's pretty rare to need direct access to unmanaged resources from within your type, and if you're using framework types they really should clean up appropriately (but non-deterministically). Still, it's possible.

猛虎独行 2024-11-11 19:49:47

系统库中的任何IDisposable都会在最终确定时自行处置(或者至少将采取与处置相同的步骤)。

对于第三方的东西没有任何保证——例如,如果您使用某个为您提供句柄的 C 库的接口,那么这些句柄可能不会被清理,直到库被卸载(这往往只在应用程序退出时发生) )。但忘记使用 Disposeusing(或者甚至不知道或不关心某种类型是一次性的)是很常见的,任何不这样做的 .net 库IMO,它的说明写得很糟糕。

Any IDisposable in the system libraries will dispose itself when finalized (or at least, will take most of the same steps as disposing would).

There's no guarantee about third-party stuff -- if you're using an interface to some C library that gives you handles, for example, those handles might not be cleaned up til the library is unloaded (which tends to only happen at app exit). But it's common enough to forget to use Dispose or using (or to not even know or care that a certain type is disposable), that any .net library that didn't account for it is pretty badly written, IMO.

雨巷深深 2024-11-11 19:49:47

创建对象时,如果它重写了 Object.Finalize,系统会将其添加到已注册终结器的对象列表中。该列表上的对象以及它们直接或间接引用的对象将不符合消除条件,除非或直到对它们调用 GC.SuppressFinalize,或者以其他方式将它们从列表中删除。如果系统注意到这样的对象有资格被消除但它存在于可终结对象列表中,系统将从已注册终结器的对象列表中删除该对象并将其添加到列表中应尽快调用其 Finalize 例程的对象。只要后一个列表非空,系统就会执行那里对象的 Finalize 例程。

请注意,垃圾收集器实际上并不处理任何内容——它所做的只是调用已注册进行终结的对象的 Finalize 例程。尽管许多类型的对象都有 Finalize 例程,可以确保在错误地放弃它们时进行清理,但终结可能会充满危险。对象很少会在完全正确的时间完成;它们通常要在被放弃很久之后才会最终确定,并且在某些情况下,它们可能在其资源仍在使用中时完成。因此,避免依赖终结器是明智的。在某些情况下这是必要的,但它们几乎总是令人讨厌。

顺便说一句,请注意,如果某个类的对象直接订阅来自使用寿命较长的对象的事件,则以前的对象(事件订阅者)以及它们直接或间接引用的任何对象都不会有资格消除或终结直到后面的对象这样做。虽然有人可能会争辩说,如果所有类被放弃,都应该自行清理,但确保事件订阅者可以发现已被放弃的类需要创建和使用包装器对象。在正确使用该类的情况下,这将增加相当大的复杂性并损害性能。在不使用包装对象时尝试使用终结器清理事件是没有意义的;在事件发布者符合资格之前,具有悬空事件的类没有资格被收集,一旦发生这种情况,前一个类是否取消订阅都不再重要。

When an object is created, if it overrides Object.Finalize, the system will add it to a list of objects with registered finalizers. Objects on that list, and objects directly or indirectly referenced by them, will be ineligible for elimination unless or until GC.SuppressFinalize is called on them, or they are otherwise removed from the list. If the system notices that such an object would be eligible for elimination but for its existence on the list of finalizable objects, the system will remove if from the list of objects with registered finalizers and add it to a list of objects which should have their Finalize routine called as soon as convenient. Whenever this latter list is non-empty, the system will execute the Finalize routines of objects there.

Note that the Garbage collector doesn't actually Dispose anything--all it does is call the Finalize routine of objects which have registered for finalization. Although many types of objects have Finalize routines which can ensure cleanup if they are wrongfully abandoned, finalization can be fraught with peril. Objects will seldom be finalized at exactly the right time; they'll often not be finalized until long after they've been abandoned, and in some cases they may be finalized while their resources are still in use. It's thus wise to avoid relying upon finalizers. There are some occasions when it's necessary, but they're almost always icky.

BTW, note that if objects of a certain class directly subscribe to events from objects with a longer useful life, neither the former objects (event subscribers), nor any objects to which they hold direct or indirect references, will become eligible for elimination or finalization until the latter objects do. While one might argue that all classes should clean up after themselves if they are abandoned, ensuring that an event subscriber can find out that's been abandoned requires the creation and use of wrapper objects. This will add considerable complexity and impair performance in the case where the class is used properly. Trying to clean up events with a finalizer when not using wrapper objects would be pointless; the class with dangling events wouldn't be eligible for collection until the event publisher became eligible, and once that happened it wouldn't matter whether the former class unsubscribed or not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文