[C#基础]理解方法的参数传递
来源:互联网 发布:知秋歌词 编辑:程序博客网 时间:2024/04/27 03:04
前几天看到论坛里有人不理解方法传递带Ref的参数与不带Ref的参数的区别,所以动手写了这篇文章。
先看下面的代码:
using System.Collections.Generic;
using System.Text;
namespace test
...{
class Program
...{
static void Main(string[] args)
...{
TestClass t = new TestClass();
t.Test();
}
}
class TestClass
...{
public void Test()
...{
int i = 1;
StringBuilder sb = new StringBuilder("InitString");
string returnvalue = Method1(i, sb);
Console.Write(string.Format("i = {0}, sb = {1}, returnvalue = {2}", i, sb, returnvalue));
Console.Read();
}
string Method1(int intvalue, StringBuilder objectvalue)
...{
intvalue = 2;
objectvalue.Append("AppendString");
objectvalue = new StringBuilder("OtherString");
return objectvalue.ToString();
}
}
}
在Test()方法中当执行到:
string returnvalue = Method1(i, sb);
这一句时,系统首先分配二个变量intvalue和objectvalue的地址(变量地址),然后读取传递过来i和sb变量的值。
1)i
因为i是值类型,所以i变量的值地址是指向堆栈上的一个地址,堆栈上的这一个地址保存的是一个直接数值。
所以读取i时,取得到的是堆栈地址上的具体数值。假设i的值地址0x0020,在堆栈地址0x0020上保存的是一个数值1。
系统为intvalue的值地址分配一个地址值如0x0040,并在堆栈地址0x0040上写入数值1。
2)sb
sb是一个引用类型,所以它的值地址是托管堆上的一个地址,这个堆地址存放的是包括实际内容的指针,
即指向实际内容的地址。所以读取sb时根据值地址,它从堆地址中读出的仍是一个内存地址。
假设sb的值地址是0x1020,在堆地址0x1020处保存是一个地址0x2020
系统为objectvalue值地址分配一个地址值如0x1040,并把地址值0x2020写入堆地址0x1040上。
所以sb和objectvalue最终都是指向地址0x2020,也就是说sb和objectvalue指向同一个对象。
所以当在方法内部使用objectvalue改变对象的内容后:
objectvalue.Append("AppendString");
sb的值也会随之变化,变为InitStringAppendString,就是由于它们最终指向的都是同一个对象
由于C#不使用指针,所以当系统读到值是地址时,会继续根据地址读取其内容,直到读取的内容不是地址为止。
可以看出方法从调用一开始,就已经与传入参数没有任何关系了。
当执行到objectvalue = new StringBuilder("OtherString");这一句时,系统分配一个新地址,如
0x8040,然后写到objectvalue值地址0x1040的内容中。
最后地址分配如下表
再说一下string类型,string是一个比较特殊的引用类型,当它从一个值改为另一个值时,总是最新分配内存。
string s = "1st";
s = "2nd";
执行s = "2nd";时相当于执行 s = new string("2nd");由于这个原因,string类型看上去很像值类型。
再看下一个例子:
using System.Collections.Generic;
using System.Text;
namespace test
...{
class Program
...{
static void Main(string[] args)
...{
TestClass t = new TestClass();
t.Test();
}
}
class TestClass
...{
public void Test()
...{
int i = 1;
StringBuilder sb = new StringBuilder("InitString");
string returnvalue = Method1(out i, ref sb);
Console.Write(string.Format("i = {0}, sb = {1}, returnvalue = {2}", i, sb, returnvalue));
Console.Read();
}
string Method1(out int intvalue, ref StringBuilder objectvalue)
...{
intvalue = 2;
objectvalue.Append("AppendString");
objectvalue = new StringBuilder("OtherString");
return objectvalue.ToString();
}
}
}
当执行int i = 1;时,系统会为i变量分配一个地址(变量地址)如0x1200, 然后为其值分配一个地址0x0020,
0x0020是分配在堆栈的一个地址,该地址的值为1
当执行StringBuilder sb = new StringBuilder("InitString");时,系统会为sb变量分配一个地址(变量地址)
如0x1220,然后为其值分配一个地址0x0040,然后创建StringBuilder类的实例并初始化,最后把这个实例内容
的地址0x2020写到地址0x0040的内容中。
调用Method1(out i, ref sb);时
1)out i
out表明它的值地址指向一个变量地址,
intvalue的值地址写入i的变量地址0x1200,
由于是out类型,所以还把i的值地址设为未初始化,即将0x0020改为0x0000(假设空地址void*为0x0000)。
执行intvalue = 2;时,在堆栈上分配一个地址0x0060再写入值2, i的值地址设为0x0060。
2)ref cb
ref与out一样,它的值地址也是指向一个变量地址
objectvalue的值地址上写入sb的变量地址0x1220
执行objectvalue.Append("AppendString");时,找到sb值地址0x0040指向的地址0x2020的对象进行操作,
这时objectvalue就可以看作sb变量了,下面objectvalue = new StringBuilder("OtherString");看作
cb = new StringBuilder("OtherString");就行了。
最后地址分配如下表
可见不用ref/out时,变动的是变量intvalue和objectvalue(参数变量)
使用ref/out后,变动的是变量i和cb(传入变量)
上面是我对方法的参数传递的理解,若有什么问题请指出。
- [C#基础]理解方法的参数传递
- c#给线程传递参数的方法
- C# 中方法参数的传递
- C#中的方法的参数传递。
- c#中三种传递参数的方法
- Java SE 基础:方法的参数传递
- C#中方法参数的引用传递、值传递。
- C#的参数传递
- C#方法参数传递-值传递
- 深入理解Java方法调用的参数传递
- 对Java方法参数按值传递的理解
- 深入理解Java中方法的参数传递机制
- 对Java方法参数按值传递的理解
- 参数传递的方法
- 方法的参数传递
- 由几行代码浅析C#的方法参数传递
- c# winform窗体之间传递参数的几种方法
- C#中向线程传递参数的方法
- 庆祝blog开通
- 图片无法显示的原因
- 演示ConstraintCollection类的使用
- JavaScript整理
- SP端SMS平台的设计
- [C#基础]理解方法的参数传递
- Memcached在大型网站中应用
- 在DLL中产生对话框的方法一(Win32 DLL)
- PHP & memcached
- my god,昨天忘写blog了;
- Never Stop Learning
- 终于把IIS装上了 长长的舒了一口气
- ......
- 考驾照的点悟