本文翻译自:Do you need to dispose of objects and set them to null?
您是否需要处理对象并将它们设置为null,或者当垃圾收集器超出范围时它们是否会将它们清理干净?
#1楼
参考:https://stackoom.com/question/chpz/你需要处理对象并将它们设置为null吗
#2楼
If they implement the IDisposable interface then you should dispose them. 如果他们实现IDisposable接口,那么你应该处理它们。 The garbage collector will take care of the rest. 垃圾收集器将处理其余的事情。
EDIT: best is to use the using
command when working with disposable items: 编辑:最好是在处理一次性物品时使用using
命令:
using(var con = new SqlConnection("..")){ ...
#3楼
You never need to set objects to null in C#. 您永远不需要在C#中将对象设置为null。 The compiler and runtime will take care of figuring out when they are no longer in scope. 编译器和运行时将负责确定它们何时不在范围内。
Yes, you should dispose of objects that implement IDisposable. 是的,您应该处理实现IDisposable的对象。
#4楼
If the object implements IDisposable
, then yes, you should dispose it. 如果对象实现了IDisposable
,那么是的,你应该处理它。 The object could be hanging on to native resources (file handles, OS objects) that might not be freed immediately otherwise. 该对象可能会挂起到本机资源(文件句柄,OS对象),否则可能无法立即释放。 This can lead to resource starvation, file-locking issues, and other subtle bugs that could otherwise be avoided. 这可能导致资源匮乏,文件锁定问题以及其他可能无法避免的细微错误。
See also Implementing a Dispose Method on MSDN. 另请参阅在MSDN上实现Dispose方法 。
#5楼
Objects will be cleaned up when they are no longer being used and when the garbage collector sees fit. 当物体不再被使用以及垃圾收集器看起来合适时,物体将被清理干净。 Sometimes, you may need to set an object to null
in order to make it go out of scope (such as a static field whose value you no longer need), but overall there is usually no need to set to null
. 有时,您可能需要将对象设置为null
以使其超出范围(例如,您不再需要其值的静态字段),但总体上通常不需要设置为null
。
Regarding disposing objects, I agree with @Andre. 关于处理对象,我同意@Andre。 If the object is IDisposable
it is a good idea to dispose it when you no longer need it, especially if the object uses unmanaged resources. 如果对象是IDisposable
, 最好在不再需要它时将其处理掉 ,特别是如果对象使用非托管资源。 Not disposing unmanaged resources will lead to memory leaks . 不处理非托管资源会导致内存泄漏 。
You can use the using
statement to automatically dispose an object once your program leaves the scope of the using
statement. 一旦程序离开using
语句的范围,就可以使用using
语句自动释放对象。
using (MyIDisposableObject obj = new MyIDisposableObject())
{
// use the object here
} // the object is disposed here
Which is functionally equivalent to: 这在功能上等同于:
MyIDisposableObject obj;
try
{
obj = new MyIDisposableObject();
}
finally
{
if (obj != null)
{
((IDisposable)obj).Dispose();
}
}
#6楼
Objects never go out of scope in C# as they do in C++. 对象永远不会像在C ++中那样超出C#的范围。 They are dealt with by the Garbage Collector automatically when they are not used anymore. 它们在不再使用时由垃圾收集器自动处理。 This is a more complicated approach than C++ where the scope of a variable is entirely deterministic. 这是一种比C ++更复杂的方法,其中变量的范围完全是确定性的。 CLR garbage collector actively goes through all objects that have been created and works out if they are being used. CLR垃圾收集器主动遍历已创建的所有对象,如果正在使用它们,则会计算出来。
An object can go "out of scope" in one function but if its value is returned, then GC would look at whether or not the calling function holds onto the return value. 对象可以在一个函数中“超出范围”,但如果返回其值,则GC将查看调用函数是否保留在返回值上。
Setting object references to null
is unnecessary as garbage collection works by working out which objects are being referenced by other objects. 将对象引用设置为null
是不必要的,因为垃圾收集通过计算其他对象正在引用哪些对象来工作。
In practice, you don't have to worry about destruction, it just works and it's great :) 在实践中,你不必担心破坏,它只是工作,它是伟大的:)
Dispose
must be called on all objects that implement IDisposable
when you are finished working with them. 必须在完成使用IDisposable
所有对象上调用Dispose
。 Normally you would use a using
block with those objects like so: 通常你会使用带有这些对象的using
块,如下所示:
using (var ms = new MemoryStream()) {
//...
}
EDIT On variable scope. 编辑可变范围。 Craig has asked whether the variable scope has any effect on the object lifetime. Craig已经询问变量范围是否对对象生命周期有任何影响。 To properly explain that aspect of CLR, I'll need to explain a few concepts from C++ and C#. 为了正确解释CLR的这个方面,我需要从C ++和C#中解释一些概念。
Actual variable scope 实际变量范围
In both languages the variable can only be used in the same scope as it was defined - class, function or a statement block enclosed by braces. 在这两种语言中,变量只能在与定义相同的范围内使用 - 类,函数或括号括起来的语句块。 The subtle difference, however, is that in C#, variables cannot be redefined in a nested block. 然而,细微差别在于,在C#中,无法在嵌套块中重新定义变量。
In C++, this is perfectly legal: 在C ++中,这是完全合法的:
int iVal = 8;
//iVal == 8
if (iVal == 8){
int iVal = 5;
//iVal == 5
}
//iVal == 8
In C#, however you get aa compiler error: 但是在C#中,您会遇到编译器错误:
int iVal = 8;
if(iVal == 8) {
int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}
This makes sense if you look at generated MSIL - all the variables used by the function are defined at the start of the function. 如果查看生成的MSIL,这是有意义的 - 函数使用的所有变量都是在函数的开头定义的。 Take a look at this function: 看看这个功能:
public static void Scope() {
int iVal = 8;
if(iVal == 8) {
int iVal2 = 5;
}
}
Below is the generated IL. 以下是生成的IL。 Note that iVal2, which is defined inside the if block is actually defined at function level. 请注意,在if块中定义的iVal2实际上是在功能级别定义的。 Effectively this means that C# only has class and function level scope as far as variable lifetime is concerned. 实际上,这意味着就可变生命周期而言,C#仅具有类和功能级别范围。
.method public hidebysig static void Scope() cil managed
{
// Code size 19 (0x13)
.maxstack 2
.locals init ([0] int32 iVal,
[1] int32 iVal2,
[2] bool CS$4$0000)
//Function IL - omitted
} // end of method Test2::Scope
C++ scope and object lifetime C ++范围和对象生存期
Whenever a C++ variable, allocated on the stack, goes out of scope it gets destructed. 每当在堆栈上分配的C ++变量超出范围时,它就会被破坏。 Remember that in C++ you can create objects on the stack or on the heap. 请记住,在C ++中,您可以在堆栈或堆上创建对象。 When you create them on the stack, once execution leaves the scope, they get popped off the stack and gets destroyed. 当您在堆栈上创建它们时,一旦执行离开作用域,它们就会从堆栈中弹出并被销毁。
if (true) {
MyClass stackObj; //created on the stack
MyClass heapObj = new MyClass(); //created on the heap
obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives
When C++ objects are created on the heap, they must be explicitly destroyed, otherwise it is a memory leak. 在堆上创建C ++对象时,必须显式销毁它们,否则会导致内存泄漏。 No such problem with stack variables though. 虽然堆栈变量没有这样的问题。
C# Object Lifetime C#对象生命周期
In CLR, objects (ie reference types) are always created on the managed heap. 在CLR中, 始终在托管堆上创建对象(即引用类型)。 This is further reinforced by object creation syntax. 对象创建语法进一步强化了这一点。 Consider this code snippet. 请考虑此代码段。
MyClass stackObj;
In C++ this would create an instance on MyClass
on the stack and call its default constructor. 在C ++中,这将在堆栈上的MyClass
上创建一个实例并调用其默认构造函数。 In C# it would create a reference to class MyClass
that doesn't point to anything. 在C#中,它将创建一个对MyClass
类的引用,它不指向任何东西。 The only way to create an instance of a class is by using new
operator: 创建类实例的唯一方法是使用new
运算符:
MyClass stackObj = new MyClass();
In a way, C# objects are a lot like objects that are created using new
syntax in C++ - they are created on the heap but unlike C++ objects, they are managed by the runtime, so you don't have to worry about destructing them. 在某种程度上,C#对象很像在C ++中使用new
语法创建的对象 - 它们是在堆上创建的,但与C ++对象不同,它们由运行时管理,因此您不必担心破坏它们。
Since the objects are always on the heap the fact that object references (ie pointers) go out of scope becomes moot. 由于对象总是在堆上,因此对象引用(即指针)超出范围的事实变得没有实际意义。 There are more factors involved in determining if an object is to be collected than simply presence of references to the object. 确定是否要收集对象涉及的因素多于简单地存在对象的引用。
C# Object references C#对象引用
Jon Skeet compared object references in Java to pieces of string that are attached to the balloon, which is the object. Jon Skeet 将Java中的对象引用与附加到气球(即对象)的字符串进行了比较。 Same analogy applies to C# object references. 相同的类比适用于C#对象引用。 They simply point to a location of the heap that contains the object. 它们只是指向包含该对象的堆的位置。 Thus, setting it to null has no immediate effect on the object lifetime, the balloon continues to exist, until the GC "pops" it. 因此,将其设置为null对对象生命周期没有立即影响,气球继续存在,直到GC“弹出”它。
Continuing down the balloon analogy, it would seem logical that once the balloon has no strings attached to it, it can be destroyed. 继续按照气球的比喻,似乎合乎逻辑的是,一旦气球没有任何附加条件,它就会被摧毁。 In fact this is exactly how reference counted objects work in non-managed languages. 实际上,这正是引用计数对象在非托管语言中的工作方式。 Except this approach doesn't work for circular references very well. 除非这种方法不适用于循环引用。 Imagine two balloons that are attached together by a string but neither balloon has a string to anything else. 想象一下两个气球通过一个字符串连接在一起,但气球都没有任何其他字符串。 Under simple ref counting rules, they both continue to exist, even though the whole balloon group is "orphaned". 在简单的引用计数规则下,它们都会继续存在,即使整个气球组是“孤立的”。
.NET objects are a lot like helium balloons under a roof. .NET对象很像屋顶下的氦气球。 When the roof opens (GC runs) - the unused balloons float away, even though there might be groups of balloons that are tethered together. 当屋顶打开(GC运行)时 - 未使用的气球会漂浮,即使可能有一组气球系在一起。
.NET GC uses a combination of generational GC and mark and sweep. .NET GC使用世代GC和标记和扫描的组合。 Generational approach involves the runtime favouring to inspect objects that have been allocated most recently, as they are more likely to be unused and mark and sweep involves runtime going through the whole object graph and working out if there are object groups that are unused. 分代方法涉及运行时有利于检查最近分配的对象,因为它们更可能未被使用,并且标记和扫描涉及运行时通过整个对象图并且如果存在未使用的对象组则进行计算。 This adequately deals with circular dependency problem. 这充分解决了循环依赖问题。
Also, .NET GC runs on another thread(so called finalizer thread) as it has quite a bit to do and doing that on the main thread would interrupt your program. 此外,.NET GC在另一个线程(所谓的终结器线程)上运行,因为它有很多工作要做,在主线程上这样做会中断你的程序。