为了账号安全,请及时绑定邮箱和手机立即绑定

您需要释放对象并将其设置为空吗?

您需要释放对象并将其设置为空吗?

杨魅力 2019-07-08 16:17:23
您需要释放对象并将其设置为空吗?您需要释放对象并将其设置为NULL吗?或者垃圾收集器会在它们超出作用域时清除它们吗?
查看完整描述

3 回答

?
小唯快跑啊

TA贡献1863条经验 获得超2个赞

对象在C#中从不超出作用域,就像它们在C+中所做的那样。当垃圾收集器不再使用时,垃圾收集器会自动处理它们。这是一种比C+更复杂的方法,在C+中,变量的范围完全是确定性的。CLR垃圾收集器主动遍历所有已创建的对象,并计算出它们是否被使用。

对象可以在一个函数中“超出作用域”,但如果返回其值,则GC将检查调用函数是否保留返回值。

将对象引用设置为null没有必要,因为垃圾收集是通过计算其他对象引用的对象来完成的。

在实践中,你不必担心破坏,它只是起作用了,而且很棒:)

Dispose必须对实现的所有对象调用IDisposable当你和他们一起工作完之后。通常您会使用using用这样的对象阻止:

using (var ms = new MemoryStream()) {
  //...}

编辑在可变范围内。Craig询问变量范围是否对象生存期有任何影响。为了正确地解释CLR的这个方面,我需要从C+和C#中解释一些概念。

实际变量范围

在这两种语言中,变量只能在定义的相同范围内使用-类、函数或用大括号括起来的语句块。然而,不同之处在于,在C#中,不能在嵌套块中重新定义变量。

在C+中,这是完全合法的:

int iVal = 8;//iVal == 8if (iVal == 8){
    int iVal = 5;
    //iVal == 5}//iVal == 8

但是,在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}

如果您查看生成的msil-函数使用的所有变量都是在函数的开头定义的,这是有意义的。看看这个函数:

public static void Scope() {
    int iVal = 8;
    if(iVal == 8) {
        int iVal2 = 5;
    }}

下面是生成的IL。注意,在if块中定义的iVal2实际上是在函数级别定义的。这实际上意味着,就变量生存期而言,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+作用域和对象生存期

每当在堆栈上分配的C+变量超出作用域时,它就会被销毁。请记住,在C+中,您可以在堆栈或堆上创建对象。当您在堆栈上创建它们时,一旦执行离开范围,它们就会从堆栈中弹出并被销毁。

if (true) {
  MyClass stackObj; //created on the stack
  MyClass heapObj = new MyClass(); //created on the heap
  obj.doSomething();} //<-- stackObj is destroyed//heapObj still lives

当在堆上创建C+对象时,必须显式销毁它们,否则就是内存泄漏。但是,堆栈变量没有这样的问题。

C#对象生命周期

在CLR中,对象(即引用类型)是在托管堆上创建。对象创建语法进一步加强了这一点。考虑一下这个代码片段。

MyClass stackObj;

在C+中,这将创建一个MyClass并调用其默认构造函数。在C#中,它将创建对类的引用MyClass这并不意味着什么。创建类实例的唯一方法是使用new操作员:

MyClass stackObj = new MyClass();

在某种程度上,C#对象非常类似于使用newC+中的语法-它们是在堆上创建的,但与C+对象不同,它们是由运行时管理的,因此您不必担心对它们进行析构。

因为对象是在堆中,对象引用(即指针)超出作用域的事实变得毫无意义。在确定是否要收集对象时,涉及的因素比简单地存在对象的引用所涉及的因素更多。

C#对象引用

乔恩·斯基特Java中的对象引用比较连接到气球上的字符串,气球是对象。同样的类比也适用于C#对象引用。它们只是指向包含对象的堆的位置。因此,将其设置为NULL对象生存期没有直接影响,气球继续存在,直到GC“弹出”它。

按照气球的类比,一旦气球没有附加条件,它就可以被摧毁,这似乎是合乎逻辑的。事实上,这正是引用计数对象在非托管语言中的工作方式。但是,这种方法对循环引用没有很好的效果。想象一下,两个气球由一根绳子连接在一起,但没有一个气球与任何其他气球相连。在简单的参考计数规则下,它们都继续存在,即使整个气球组是“孤儿”。

NET对象很像屋顶下的氦气气球。当屋顶打开(GC运行)

NETGC使用分代GC和标记和扫描的组合。分代方法包括运行时倾向于检查最近分配的对象,因为它们更可能未使用;标记和扫描涉及运行时遍历整个对象图,并计算出是否有未使用的对象组。这充分地处理了循环依赖问题。

此外,.NET GC在另一个线程上运行(即所谓的终结器线程),因为它有相当多的工作要做,在主线程上这样做会中断程序。


查看完整回答
反对 回复 2019-07-08
?
临摹微笑

TA贡献1982条经验 获得超2个赞

就像其他人说的,你绝对想打电话给Dispose如果类实现IDisposable..我对此采取了相当强硬的立场。有些人可能会声称Dispose在……上面DataSet例如,因为他们拆掉了它,看到它没有做任何有意义的事情,所以是毫无意义的。但是,我认为这个论点中有很多谬误。

朗读,阅读这,这个值得尊敬的人就这个问题进行有趣的辩论。那就读读我的推理这里为什么我认为杰弗里·里克特在错误的阵营。

现在,关于是否应该将引用设置为null..答案是否定的。让我用下面的代码来说明我的观点。

public static void Main(){
  Object a = new Object();
  Console.WriteLine("object created");
  DoSomething(a);
  Console.WriteLine("object used");
  a = null;
  Console.WriteLine("reference set to null");}

那么你认为什么时候被引用的对象a是否有资格领取?如果你说打完电话a = null那你就错了。如果你说在Main方法完成,那么您也错了。正确的答案是,它有时有资格领取。期间打电话给DoSomething..这是对的。它有资格以前引用设置为null甚至在呼吁DoSomething完成。这是因为即使对象引用仍然是根,JIT编译器也可以识别对象引用何时不再取消引用。


查看完整回答
反对 回复 2019-07-08
  • 3 回答
  • 0 关注
  • 452 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信