C# 将对象设置为 null 与 Dispose()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/574019/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Setting an object to null vs Dispose()
提问by GurdeepS
I am fascinated by the way the CLR and GC works (I'm working on expanding my knowledge on this by reading CLR via C#, Jon Skeet's books/posts, and more).
我对 CLR 和 GC 的工作方式很着迷(我正在通过 C#、Jon Skeet 的书籍/帖子等阅读 CLR 来扩展我的知识)。
Anyway, what is the difference between saying:
无论如何,这句话有什么区别:
MyClass myclass = new MyClass();
myclass = null;
Or, by making MyClass implement IDisposable and a destructor and calling Dispose()?
或者,通过让 MyClass 实现 IDisposable 和析构函数并调用 Dispose()?
Also, if I have a code block with a using statement (eg below), if I step through the code and exit the using block, is the object disposed of then or when a garbage collection occurs? What would happen if I call Dispose() in the using block anyay?
另外,如果我有一个带有 using 语句的代码块(例如下面),如果我单步执行代码并退出 using 块,那么对象是在那时还是在发生垃圾收集时被处理?如果我在 using 块中调用 Dispose() 会发生什么?
using (MyDisposableObj mydispobj = new MyDisposableObj())
{
}
Stream classes (eg BinaryWriter) have a Finalize method? Why would I want to use that?
流类(例如 BinaryWriter)有一个 Finalize 方法?我为什么要使用它?
采纳答案by Jon Skeet
It's important to separate disposal from garbage collection. They are completely separate things, with one point in common which I'll come to in a minute.
将处理与垃圾收集分开很重要。它们是完全不同的事物,有一点是相同的,我稍后会谈到。
Dispose
, garbage collection and finalization
Dispose
,垃圾收集和终结
When you write a using
statement, it's simply syntactic sugar for a try/finally block so that Dispose
is called even if the code in the body of the using
statement throws an exception. It doesn'tmean that the object is garbage collected at the end of the block.
当您编写using
语句时,它只是 try/finally 块的语法糖,因此Dispose
即使using
语句主体中的代码抛出异常也会调用它。这并不意味着该对象在块的末尾被垃圾收集。
Disposal is about unmanaged resources(non-memory resources). These could be UI handles, network connections, file handles etc. These are limited resources, so you generally want to release them as soon as you can. You should implement IDisposable
whenever your type "owns" an unmanaged resource, either directly (usually via an IntPtr
) or indirectly (e.g. via a Stream
, a SqlConnection
etc).
处置是关于非托管资源(非内存资源)。这些可能是 UI 句柄、网络连接、文件句柄等。这些是有限的资源,因此您通常希望尽快释放它们。IDisposable
每当您的类型“拥有”非托管资源时,您都应该直接(通常通过 an IntPtr
)或间接(例如通过 a Stream
、 aSqlConnection
等)实施。
Garbage collection itself is only about memory - with one little twist. The garbage collector is able to find objects which can no longer be referenced, and free them. It doesn't look for garbage all the time though - only when it detects that it needs to (e.g. if one "generation" of the heap runs out of memory).
垃圾收集本身仅与内存有关 - 稍加改动。垃圾收集器能够找到不再被引用的对象,并释放它们。不过,它不会一直寻找垃圾——只有当它检测到它需要时(例如,如果堆的“一代”耗尽了内存)。
The twist is finalization. The garbage collector keeps a list of objects which are no longer reachable, but which have a finalizer (written as ~Foo()
in C#, somewhat confusingly - they're nothing like C++ destructors). It runs the finalizers on these objects, just in case they need to do extra cleanup before their memory is freed.
转折点是定稿。垃圾收集器保留了一个不再可访问但具有终结器的对象列表(用~Foo()
C#编写,有点令人困惑——它们与 C++ 析构函数完全不同)。它在这些对象上运行终结器,以防它们在释放内存之前需要进行额外的清理。
Finalizers are almost always used to clean up resources in the case where the user of the type has forgotten to dispose of it in an orderly manner. So if you open a FileStream
but forget to call Dispose
or Close
, the finalizer will eventuallyrelease the underlying file handle for you. In a well-written program, finalizers should almost never fire in my opinion.
在该类型的用户忘记以有序的方式处理资源的情况下,终结器几乎总是用于清理资源。所以,如果你打开一个FileStream
却忘记打电话Dispose
或者Close
,终结将最终释放底层文件为您处理。在一个编写良好的程序中,在我看来,终结器几乎不应该被触发。
Setting a variable to null
将变量设置为 null
One small point on setting a variable to null
- this is almost never required for the sake of garbage collection. You might sometimes want to do it if it's a member variable, although in my experience it's rare for "part" of an object to no longer be needed. When it's a local variable, the JIT is usually smart enough (in release mode) to know when you're not going to use a reference again. For example:
关于将变量设置为的一个小点null
- 为了垃圾收集,这几乎不需要。如果它是一个成员变量,您有时可能想要这样做,尽管根据我的经验,不再需要对象的“部分”很少见。当它是一个局部变量时,JIT 通常足够聪明(在发布模式下)知道何时不再使用引用。例如:
StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();
// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);
// These aren't helping at all!
x = null;
sb = null;
// Assume that x and sb aren't used here
The one time where it maybe worth setting a local variable to null
is when you're in a loop, and some branches of the loop need to use the variable but you know you've reached a point at which you don't. For example:
一个时间它可能是值得设置一个局部变量null
是当你在一个循环,并且循环需要一些分支机构使用的变量,但你知道你已经达到在你做的不是一个点。例如:
SomeObject foo = new SomeObject();
for (int i=0; i < 100000; i++)
{
if (i == 5)
{
foo.DoSomething();
// We're not going to need it again, but the JIT
// wouldn't spot that
foo = null;
}
else
{
// Some other code
}
}
Implementing IDisposable/finalizers
实现 IDisposable/终结器
So, should your own types implement finalizers? Almost certainly not. If you only indirectlyhold unmanaged resources (e.g. you've got a FileStream
as a member variable) then adding your own finalizer won't help: the stream will almost certainly be eligible for garbage collection when your object is, so you can just rely on FileStream
having a finalizer (if necessary - it may refer to something else, etc). If you want to hold an unmanaged resource "nearly" directly, SafeHandle
is your friend - it takes a bit of time to get going with, but it means you'll almostnever need to write a finalizer again. You should usually only need a finalizer if you have a really direct handle on a resource (an IntPtr
) and you should look to move to SafeHandle
as soon as you can. (There are two links there - read both, ideally.)
那么,您自己的类型是否应该实现终结器?几乎可以肯定不是。如果您只是间接持有非托管资源(例如,您将 aFileStream
作为成员变量),那么添加自己的终结器将无济于事:当您的对象存在时,流几乎肯定有资格进行垃圾回收,因此您可以依靠FileStream
有一个终结器(如果有必要——它可能指的是别的东西,等等)。如果您想“几乎”直接持有一个非托管资源,那么SafeHandle
您的朋友 - 需要一些时间来开始,但这意味着您几乎不需要再次编写终结器。如果您对资源 (an IntPtr
)有真正的直接处理,您通常只需要一个终结器,并且您应该考虑转向SafeHandle
你尽快做。(那里有两个链接 - 最好同时阅读。)
Joe Duffy has a very long set of guidelines around finalizers and IDisposable(co-written with lots of smart folk) which are worth reading. It's worth being aware that if you seal your classes, it makes life a lot easier: the pattern of overriding Dispose
to call a new virtual Dispose(bool)
method etc is only relevant when your class is designed for inheritance.
Joe Duffy有一套很长的关于终结器和 IDisposable 的指南(与许多聪明人合着),值得一读。值得注意的是,如果你密封你的类,它会让生活变得更容易:覆盖Dispose
调用新的虚拟Dispose(bool)
方法等的模式只有当你的类是为继承而设计时才相关。
This has been a bit of a ramble, but please ask for clarification where you'd like some :)
这有点啰嗦,但请澄清您想要的地方:)
回答by recursive
When you dispose an object, the resources are freed. When you assign null to a variable, you're just changing a reference.
处置对象时,将释放资源。当您将 null 分配给变量时,您只是在更改引用。
myclass = null;
After you execute this, the object myclass was referring to still exists, and will continue to until the GC gets around to cleaning it up. If Dispose is explicitly called, or it's in a using block, any resources will be freed as soon as possible.
执行此操作后, myclass 所指的对象仍然存在,并将继续存在,直到 GC 开始清理它。如果显式调用 Dispose,或者它在 using 块中,则将尽快释放任何资源。
回答by jalf
The two operations don't have much to do with each others. When you set a reference to null, it simply does that. It doesn't in itself affect the class that was referenced at all. Your variable simply no longer points to the object it used to, but the object itself is unchanged.
这两个操作彼此没有太大关系。当您将引用设置为 null 时,它只是这样做。它本身根本不影响被引用的类。你的变量不再指向它曾经指向的对象,但对象本身没有改变。
When you call Dispose(), it's a method call on the object itself. Whatever the Dispose method does, is now done on the object. But this doesn't affect your reference to the object.
当您调用 Dispose() 时,它是对对象本身的方法调用。无论 Dispose 方法做什么,现在都在对象上完成。但这不会影响您对对象的引用。
The only area of overlap is that whenthere are no more references to an object, it will eventuallyget garbage collected. And if the class implements the IDisposable interface, then Dispose() will be called on the object before it gets garbage collected.
唯一的重叠区域是当没有更多的对象引用时,它最终会被垃圾回收。如果该类实现了 IDisposable 接口,则将在对象被垃圾收集之前调用 Dispose()。
But that won't happen immediately after you set your reference to null, for two reasons. First, other references may exist, so it won't get garbage collected at all yet, and second, even if that was the last reference, so it is now ready to be garbage collected, nothing will happen until the garbage collector decides to delete the object.
但这不会在您将引用设置为 null 后立即发生,原因有二。首先,其他引用可能存在,所以它根本不会被垃圾收集,其次,即使那是最后一个引用,所以它现在准备好被垃圾收集,直到垃圾收集器决定删除之前什么都不会发生物体。
Calling Dispose() on an object doesn't "kill" the object in any way. It is commonly used to clean up so that the object canbe safely deleted afterwards, but ultimately, there is nothing magical about Dispose, it's just a class method.
在对象上调用 Dispose() 不会以任何方式“杀死”对象。它通常被用来使清理对象可以被安全地删除之后,但最终,有关于处置什么神奇,它只是一个类的方法。