C# - 对象范围

Pau*_*bes 8 c#

我看过这个这个,我有以下问题,看看我是否理解正确.鉴于代码

using System;

namespace returnObject
{
    class myObject
    {
        public int number { get; set; }

    }

    class Program
    {
        static void Main(string[] args)
        {

            myObject mainObj = make();
            mainObj.number = 7;
        }

        public static myObject make()
        {
            myObject localObj = new myObject();
            localObj.number = 4;
            return localObj;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我希望localObj在make方法结束时超出范围,因此,obj.number在main函数中设置为7会失败.它没有.我认为我说的是正确的:

  • localObj 是对象的引用
  • localObj 在堆栈上创建
  • localObj 在make方法结束时超出范围.
  • localObj引用的对象在堆上.

所以,我正确地认为通常所localObj引用的对象在make方法结束时被垃圾收集器标记为删除但是由于引用值已被传递回mainObj,因此该对象被引用,因此不符合条件删除?

另外是以这种方式创建对象的好习惯吗?

Eri*_*ert 14

我是否正确地认为通常localObj引用的对象在make方法的末尾被垃圾收集器标记为删除但是由于引用值已经传递回mainObj,因此引用该对象,因此不符合条件删除?

这是一个非常复杂的问题,但它的形式只承认是或否答案.不要试图回答这个问题,而是让我将其细分为原点.

localObj是对象的引用

更好:localObj是一个局部变量.局部变量是引用类型的存储位置.存储位置包含对象的引用,或者为null.

localObj在堆栈上创建

正确,虽然这是一个实现细节.与变量关联的存储位置是从临时池分配的.作为实现细节,CLR使用调用堆栈作为临时池.它不需要; 堆栈只是一种便宜,方便的方式来获得临时池.

localObj在make方法结束时超出范围.

正确.变量的范围定义为程序文本的区域,可以通过其非限定名称使用它.此特定变量的范围是方法的整个主体.(我还注意到,在C#中,传统的方法,属性和类型都是大写字母,因为你没有这样做.)

localObj引用的对象是在堆上.

正确.作为实现细节,所有引用都是null,或者引用长期存储中的对象,也就是托管堆.CLR的实现可以使用流分析来确定特定对象不会转义保存对其唯一引用的局部变量的生命周期,因此将其分配给临时池.在实践中,这在我所知道的任何实现中都不会发生.

如果没有返回,localObj引用的对象将被make方法末尾的垃圾收集器标记为删除

.这是你的第一个重大错误印象.GC不具有确定性.它没有立即看到局部变量超出范围,因此对象已被孤立.该对象继续存在,直到某个策略触发垃圾回收.即使这样,该对象也可能在之前的集合中幸存下来并被提升为后一代.后代的收集频率较低.所有你知道的是,对象将不再被标记为生存.请记住,标记扫描垃圾收集器标记对象以便生存,而不是删除.

此外,在make方法结束之前,GC对象进行清理是完全合法的.在处理托管/非托管代码互操作时,这是导致错误的常见原因.请考虑以下情形:

void M() 
{
    FileThingy f = GetFileThingy();
    MyUnmanagedLibrary.ConsumeFileThingy(f);
}
Run Code Online (Sandbox Code Playgroud)

什么时候可以收藏?一旦GC确定没有托管代码再次消耗该引用.但是,在非托管代码获取其引用副本之后的瞬间,任何托管代码都不会使用此引用,因此GC在调用调用之后但在托管代码运行之前立即在另一个线程上收集对象的权限.要解决此问题,您需要使用KeepAlive来保持对象的活动.

当局部变量超出范围时,您不能依赖GC回收存储.对象的生命周期极有可能比可变的寿命更长,这是合法的它更短,如果GC能够证明托管代码不能看出其中的差别.

由于引用值已传递回mainObj,因此引用该对象,因此不符合删除条件

正确.但是,再次假设mainObj例程没有使用传回来的对象.抖动在其权利范围内,可以注意到这一事实,并优化了未使用数据的遍历,从而使对象可以用于早期收集.

我在这里得到的是垃圾收集器应该被认为比管理你的记忆更聪明.该物体将在它需要的时候消失,并且可能在它可能的时间之后消失,但可能比你想象的要早.不要担心,要学会热爱不确定性; GC正在为您管理事情并且它做得很好.


SLa*_*aks 3

C# 不像 C 或 C++ 那样工作;对象永远不会在堆栈上分配。

所有对象(引用类型)都存在于垃圾收集堆上;垃圾收集器将在对象不再被引用一段时间后收集它们。

如果不使用弱引用,则根本不可能在 GC 后观察托管对象。