c# - 可变生命周期

标签 c# lifetime object-lifetime lifetime-scoping

当执行行超出代码块时,变量会发生什么变化? 例如:

1  public void myMethod()
2  {
3     int number;
4     number = 5;
5  }

因此,我们声明并设置变量。当它超出代码块(第 5 行)时,变量号会发生什么变化?

这是另一个创建类实例的例子:

7   public void myMethod()
8   {
9      Customer myClient;
10     myClient = new Customer();
11  }

当它超出代码块(第 11 行)时,对象引用 myClient 会发生什么?

我想在这两种情况下变量都被分配了,但是什么时候被释放了?

最佳答案

作为变量,它是C# 语言中的一个概念。在代码块之外没有任何“发生”,因为它在代码块内。这句话之外的单词 word 没有任何变化。

当然,您的意思是代码运行时变量会发生什么变化,但值得记住这种区别,因为在考虑这个问题时,我们正在转向变量与 C# 中不同的级别。

在这两种情况下,代码都被转换为 CIL,然后在运行时转换为机器码。

CIL 可能会有很大差异。例如,下面是第一个在 Debug模式下编译时的样子:

.method public hidebysig instance void myMethod () cil managed 
{
  .locals init ([0] int32) // Set-up space for a 32-bit value to be stored
  nop                      // Do nothing
  ldc.i4.5                 // Push the number 5 onto the stack
  stloc.0                  // Store the number 5 in the first slot of locals
  ret                      // Return
}

下面是编译发布时的样子:

.method public hidebysig instance void myMethod () cil managed 
{
  ret                      // Return
}

由于未使用该值,编译器将其作为无用的垃圾删除并仅编译一个立即返回的方法。

如果编译器没有删除这样的代码,我们可能会想到:

.method public hidebysig instance void myMethod () cil managed 
{
  ldc.i4.5                 // Push the number 5 onto the stack
  pop                      // Remove value from stack
  ret                      // Return
}

调试构建存储东西的时间更长,因为检查它们对调试很有用。

当发布版本确实将内容存储在本地数组中时,它们也更有可能在方法中重用槽。

然后将其转换为机器代码。它的工作方式类似,它要么产生数字 5,将其存储在本地(在堆栈或寄存器中),然后再次删除它,要么什么都不做,因为未使用的变量已被删除. (也许甚至不执行该方法;可以内联该方法,然后由于它不执行任何操作而被有效地完全删除)。

对于带有构造函数的类型,还有更多的事情要做:

.method public hidebysig instance void myMethod () cil managed 
{
  .locals init ([0] class Temp.Program/Customer)       // Set-up space for a reference to a Customer

  nop                                                  // Do nothing.
  newobj instance void SomeNamespace/Customer::.ctor() // Call Customer constructor (results in Customer on the stack)
  stloc.0                                              // Store the customer in the frist slot in locals
  ret                                                  // Return
}

.method public hidebysig instance void myMethod () cil managed 
{
  newobj instance void SomeNamespace/Customer::.ctor() // Call Customer constructor (results in Customer on the stack)
  pop                                                  // Remove value from stack
  ret                                                  // Return
}

这里都调用构造函数,甚至发布版本也会调用构造函数,因为它必须确保任何副作用仍然发生。

如果 Customer 是引用类型,还会发生更多情况。如果它是一个值类型,那么它的所有内容都保存在堆栈中(尽管它可能具有依次为引用类型的字段)。如果它是引用类型,那么堆栈中保存的是对堆中对象的引用。当堆栈上不再有任何此类引用时,垃圾收集器将不会在其扫描中找到它以查找它无法收集的对象,并且可以收集它。

在发布版本中,一旦构造函数返回,可能永远不会有存储该引用的内存位置或寄存器。实际上,即使构造函数正在运行(如果没有字段访问或对 this 的其他隐式或显式使用发生),也可能没有一个,或者它可能已经被部分删除(一旦这样的访问已经完成),因此垃圾收集甚至可能在构造函数完成之前发生。

更有可能在方法返回后它会在堆内存中停留一段时间,因为 GC 尚未运行。

关于c# - 可变生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31137225/

相关文章:

C# 使用静态变量作为 DeploymentItem 的参数

c# - 如何将动画 gif 添加到按钮?

C# 组合框重写 ToString

c - 从另一个函数调用构造函数时的存储持续时间

c++ - 通过placement-new手动构造一个平凡的基类

c# - 如何在if条件中应用==十进制值?

rust - 解决通过引用获取参数的闭包的类型不匹配

rust - 初始化结构的可变引用数组

C++ 对象在堆栈和堆上的生命周期

c++ - C++ 函数中 "return"的确切时刻