先贴一段代码:
class Program
{
static void Main(string[] args)
{
string ss = "asdf";
operate(ss);
Console.WriteLine(ss);
Console.Read();
}
public static void operate(string aa)
{
aa = "sdf";
Console.WriteLine(aa);
}
}
输出是:
sdf
asdf
先解释这个东西,在.net中值类型是存放在堆栈中的,引用类型分为两部分存储,一部分是指针存储在堆栈中,另外一部分是类型的实例存储的堆中.
然后在说下
string ss = "asdf";
这一句代码发生了什么,首先.net在栈中分配了一个string类型的指针所需要的地址,它没有指向任何东西.然后.net在堆中查找有没有"asdf"这个string的实例,如果没有它就要新建立一个string实例为asdf然后把此实例的地址赋给刚才在栈中分配的那个指针,这就是等号的作用.
然后我们在看operate(ss);这一句代码发生了什么.这个地方传递过来的是堆里面asdf的地址,然后.net又在栈上分配了一个string类型的指针所需要的地址,这一块地址就叫aa,但是呢这个aa中是有值的它的值就是刚才我们给asdf分配在堆上的内存区域的值.
接着 aa = "sdf";由于已经给aa在栈上分配了内存区域,所以直接在堆上找有没有sdf这个实例,没有! 好重新拿出一块来存放sdf,然后把这块的内存地址放到刚才给aa分配的地址里面.
后面的两个输出就简单了,只要分别输出栈里指针所指向的堆里面的值就可以了.
以上是没有加ref的过程.
另外一段代码:
class Program
{
static void Main(string[] args)
{
string ss = "asdf";
operate(ref ss);
Console.WriteLine(ss);
Console.Read();
}
public static void operate(ref string aa)
{
aa = "sdf";
Console.WriteLine(aa);
}
}
输出两行 sdf
第一句赋值语句不解释了,跟第一种一样.现在来看第二句operate(ref ss);
多了一个ref那传递过来的东西就不一样了,这里传递过来的是ss所在内存的地址,然后aa = "sdf";的时候还是要在堆上找有没有sdf这个实例,没有的话重新创建,分配堆地址.
这个地方就是不一样的了,.net将这个分配好的地址编号,赋值给了ss所在的栈地址,而没有将此地址赋值给aa所在的栈地址.所以我们从方法返回后ss所指向的堆地址改变了.所以它的值也改变了