作为变量,它是 C# 语言中的一个概念。在代码块之外没有任何“发生”,因为它在代码块内。这个句子之外的单词word没有任何反应。
当然,您的意思是当代码运行时变量会发生什么变化,但值得记住区别,因为在考虑这个问题时,我们正在转移到变量与 C# 中不同的级别。
在这两种情况下,代码都会转换为 CIL,然后在运行时转换为机器代码。
CIL 可能有很大的不同。例如,这里是第一个在调试模式下编译时的样子:
.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
}
Debug 构建存储东西的时间更长,因为检查它们对调试很有用。
当发布版本确实将内容存储在本地数组中时,它们也更有可能在方法中重用插槽。
然后将其转换为机器代码。它的工作方式类似于它会产生数字 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
是引用类型,还会发生更多情况。如果它是一个值类型,那么所有它都保存在堆栈中(尽管它可能具有依次是引用类型的字段)。如果它是引用类型,那么堆栈中保存的是对堆中对象的引用。当堆栈上不再有任何此类引用时,垃圾收集器将不会在其扫描中找到它以查找它无法收集的对象,并且可以将其收集。
在发布版本中,一旦构造函数返回,可能永远不会有保存该引用的内存位置或寄存器。事实上,即使在构造函数运行时也可能没有一个(如果没有字段访问或其他隐式或显式使用发生),或者它可能已被部分擦除(一旦此类访问完成),所以垃圾收集可能在构造函数完成之前发生。
更有可能在方法返回后它会在堆内存中停留一段时间,因为 GC 还没有运行。