C# 将方法内部局部变量的引用指定给公共静态变量时会发生什么情况
同时,在互联网上阅读时,我了解到静态变量总是有相同的内存地址。因此,在编译程序时,编译器将决定分配给静态变量的内存地址让我想到当你这样做时会发生什么的阅读:C# 将方法内部局部变量的引用指定给公共静态变量时会发生什么情况,c#,static,heap-memory,stack-memory,C#,Static,Heap Memory,Stack Memory,同时,在互联网上阅读时,我了解到静态变量总是有相同的内存地址。因此,在编译程序时,编译器将决定分配给静态变量的内存地址让我想到当你这样做时会发生什么的阅读: class Foo {} class Program { public static Foo someStaticVar; static void Main(string[] args) { Foo localVariable = new Foo(); int x = 4;
class Foo {}
class Program
{
public static Foo someStaticVar;
static void Main(string[] args)
{
Foo localVariable = new Foo();
int x = 4;
someStaticVar = localVariable; // is someStaticVariable still will have the same address?
}
// variable x will be pushed of the stack
// localVariable will be pushed of the stack
// what happens then with someStatic var?
}
我还了解到,当在方法中声明变量时,它们将在创建时被推送到堆栈中,并在方法返回时弹出堆栈。如果所有这些都是真的,那么someStaticVar应该消失,但事实并非如此
我肯定我理解错了什么。或者在
somestativar=localVariable行上
正在执行该对象的深度复制,但我对此表示怀疑,因为interenet上有很多关于如何进行对象深度复制的问题,它们与此方法有很大不同 在您提供的示例中,localVariable
只是Main
方法的局部变量。一旦主方法结束执行,它就不在范围内了。但由于您已将其分配给静态字段,因此创建的Foo实例将继续位于Main方法之外。因为它是静态字段,所以它甚至会存在于程序类之外
下面是一步一步发生的事情:
static void Main(string[] args)
{
// an instance of Foo is created and pushed on the heap
// localVariable is now pointing to the address of this instance
// localVariable itself is stored on the stack
Foo localVariable = new Foo();
// someStaticVar is now pointing to the same location on the heap as
// the localVariable - the Foo instance created earlier
someStaticVar = localVariable;
}
// at this stage the Main method finishes executing and all local
// variables are falling out of scope. But since you have a static variable
// pointing to the Foo instance that was created inside the Main method this
// instance is not illegible for garbage collection because there are still
// references to it (someStaticVar).
如果
someStaticVar
是一个实例字段,并且不是静态的,那么将发生相同的过程,只是一旦不再有对包含类(程序)的任何引用,实例将脱离范围。对象没有深度复制:对象本身存储在堆上,而不是堆栈上。您认为someStaticVariable
将始终具有相同的地址是正确的。我相信你的误会就在那个地址里
当您声明引用类型的变量(任何对象
类型,例如代码中的Foo
)时,变量本身不是对象:它是存储对象地址的变量。那个地址只是一个号码。所以,“someStaticVariable的地址”不是Foo本身的地址;这是Foo
的地址
同样,这也是为什么
someStaticVar
不会“消失”的原因。代码中发生的事情是在内存中的某个地址创建Foo
,并且localVariable
被设置为表示Foo
内存中地址的值。执行somestativar=localVariable
时,发生的情况是地址从localVariable
复制到somestativar
,因此两个变量指向相同的Foo
。当localVariable
消失时,someStaticVar
不受影响。在本例中,Foo
是一个类,因此它是一个引用类型。Foo
类型的变量实际上只是指向内存中实际存储Foo
对象的位置的指针
当说静态变量有一个恒定的内存地址时,它是而不是说该内存地址的值是恒定的。指向Foo
对象的指针地址不会改变,但存储在固定内存位置的数字(在本例中为地址)很容易在各种潜在Foo
对象(或null
)之间发生变化
除此之外,由于C#是一种托管语言,而不是一种非托管语言,所以说静态变量将有一个恒定的内存地址是不正确的。这当然是可能的,但它实际上是C#内存管理器的一个实现细节,您不应该依赖它。在方法中声明变量时,它们将在创建时被推送到堆栈中,并在方法返回时弹出堆栈 Foo localVariable=new Foo() 将在堆上创建Foo对象,&引用存储在堆栈上。方法完成后,将从堆栈中删除引用。垃圾收集器将执行从堆中删除Foo的工作,因为不会引用Foo对象。 但是, someStaticVar=localVariable ,将导致someStaticVar引用堆上的Foo对象。即使在方法退出之后,someStaticVar仍将引用Foo对象。所以,垃圾收集器不会收集那个Foo对象。 要记住的主要事情是,创建引用类型的对象时,对象在堆上创建,引用存储在堆栈上。
问题是“静态字段存储在哪里?”,对象的实例字段存储在堆上,局部变量存储在堆栈上,但“静态变量存在于内存中?”在托管语言中,“内存地址”毫无意义。因此,不,编译器没有为C#中的任何变量预先分配内存地址。我认为,认为
Foo
的实例是在堆栈上创建的是误导性的——对象是在堆上创建的;对它的引用存储在堆栈上。@DanPuzey,你完全正确。谢谢你指出这一点。我更新了我的答案,以考虑到你的评论。