c#默认的传参方式是值传递。但是有一些不明白的地方。
public void RefereneParameter()
{
string temp = "123"; Console.Write(temp); ChangeReferenceValue(temp); Console.Write(temp); Console.Read();
}
private void ChangeReferenceValue(int inValue)
{
inValue = 456;
}
private void ChangeReferenceValue(string inValue)
{
inValue = "456";
}
第一个函数可以理解传入的是值类型的参数所以 两次输出结果相同,
但是第二个呢 参数是string 它本身就是引用类型,传入的本身就是一个地址,在里面修改后应该是改的托管堆里数据。为什么在输出时值不变呢
第二个函数当我使用ref时输出才变,难道不使用ref时 ,string 类型传入的也是一个值类型,觉得不可能
请问当参数是string时,传入的是什么呢
public void RefereneParameter()
{
string temp = "123"; Console.Write(temp); ChangeReferenceValue(temp); Console.Write(temp); Console.Read();
}
private void ChangeReferenceValue(int inValue)
{
inValue = 456;
}
private void ChangeReferenceValue(string inValue)
{
inValue = "456";
}
第一个函数可以理解传入的是值类型的参数所以 两次输出结果相同,
但是第二个呢 参数是string 它本身就是引用类型,传入的本身就是一个地址,在里面修改后应该是改的托管堆里数据。为什么在输出时值不变呢
第二个函数当我使用ref时输出才变,难道不使用ref时 ,string 类型传入的也是一个值类型,觉得不可能
请问当参数是string时,传入的是什么呢
str = "234" //这是str已经指向了另一个新的字符串,而不是把原来的字符串从123改为234
引用类型是在堆栈里放一个地址,而在托管堆里存值,我传参传入的是一个堆栈里的地址,
string str = "123";
str = "234" //这是str已经指向了另一个新的字符串,而不是把原来的字符串从123改为234
我明白是另外一个地址,前一个地址废弃了等待gc回收,但是这个在托管堆里的内存它应该是和传入的那个地址对应的,也就是传入的那个地址不再指向123 所在的地址,而是指向了234所在的地址,
不知道自己说的对不对。
高手指教
{
inValue = "456";
}
这个函数调用大致是这样的
invalue 这个地址压栈
call ChangeReferenceValue
inValue 这个变量指向了新的地址 "456"
end
invalue 原先的地址出栈,覆盖了在函数中改变的地址 ,又指回了原来的"123";
{
public int a;
}
test(class1 c1 )
{
c1.a = 10;
}
test1(class1 c1)
{
c1 = new class1();
c1.a = 100;
}class1 temp = new class1();
test(temp); 调用后temp.a =10;
test1(temp);
调用后 temp 不变
当把一个字符串变量富给另一个字符串时,会得到对内存忠同一个字符串两个引用,但是,如果修改其中一个字符串,就会创建一个全新的string对象,而另一个字符串没有改变。
总之,把string类型的对象大概的看成值类型操作就对啦!
你的程序;
public void RefereneParameter()
{
string temp = "123"; //例如:temp是堆上0x123456的引用
Console.Write(temp);
ChangeReferenceValue(temp);//此时参数intValue也指向了0x123456,而当进入函数后,intValue = "123"; 此时intValue不在等
Console.Write(temp); //0x123456, 这是string类的特点, 每次对它的修改都会在堆上新创建一片空间,然后指向它
Console.Read(); //所以intValue 指向了别的地方, 原来的内存内容并没有被修改 }
public class TypeTransition
{
private int a;
public TypeTransition()
{
a = 123;
} public void ReferenceParameter2()
{
Console.Write(a);
TypeTransition t = new TypeTransition();
ChangeReferenceValue(t);
Console.Write(a);
Console.Read();
}
private void ChangeReferenceValue(TypeTransition t)
{ t.a = 456;
}
}
这个例子输出后依然是 123 123
c#默认是值类型传递,难道就是这个意思,任何改变都在函数里改变,进入函数后重新分配内存,当出这个函数后,这个地址依然指向进入函数前的那块内存?
private void ChangeReferenceValue(string inValue)
{
inValue = "456";
}
中 并不是改变了原实参"123"的引用地址 而是新创建了(或者是在字符串池中取到了已经存在的)形参"456"
我是这样理解的
不会这样吧。如果真的是传入的一个完整的副本,一个简单的string类型还好,如果是一个大的自定义结构怎么办,难道也会在内存里重新分配一块内存出来?
这是string与其它引用类型不一样的地方。
Console.Write(a);
ChangeReferenceValue(this);
Console.Write(a);
test1:
protected void ff(StringBuilder s)
{
s.Append( "1000");
} protected void Test001(object sender, EventArgs e)
{
StringBuilder s = new StringBuilder( "asd");
ff(s);
Response.Write(s); //返回 "asd1000"
}test2:
protected void ff(string s)
{
s += "1000";
} protected void Test001(object sender, EventArgs e)
{
string s = "asd";
ff(s);
Response.Write(s); //返回asd
}string传送没有修改的真正原因是因为 string是引用类型.而且需要注意 "aaa"这个也是一个string对象.首先看这句:
string s1 = "aaa";
string s2 = s1;这里当把一个字符串变量赋值给另一个字符串时,会得到内存中同一个字符的两个引用.没什么问题.然后s1 = "222";这个时候需要注意,这里先创建了一个字符对象 "222" ,然后再将s1的引用指向新的字符对象,而原来的s2的引用依旧指向 "aaa".看似诡异,其实重点在于赋值的语法. 正是由于string是引用类型,所以在赋值的时候都会创建一个新的字符对象,string只是修改了引用的地址.原来地址中的内容不会被覆盖或者修改.
不好意思,是我弄错了,看上面的代码 我第一次输出的是构造函数里输出的值,但是紧接着我又new了一个对象,当我把这个new的对象传入函数时当然不会修改我构造函数里的值了。
谢谢回复。
看了16楼的话,“看似诡异,其实重点在于赋值的语法. 正是由于string是引用类型,所以在赋值的时候都会创建一个新的字符对象,string只是修改了引用的地址.原来地址中的内容不会被覆盖或者修改. ”
我明白你的意思,但是请看你写的这个例子
protected void ff(string s)
{
s += "1000";
}
这里string的参数s的地址是什么呢
难道不是在
protected void Test001(object sender, EventArgs e)
{
string s = "asd";
ff(s);
Response.Write(s); //返回asd
}
这个“asd”指向的指针吗?
如果是按照你的说法“string只是修改了引用的地址.”我会把第一函数传入的地址变成字符串“asd1000”所在的地址,但是为什么还是指向以前的asd呢,还是出了这个函数,地址重新被还原呢。请看4楼谢谢回复
http://topic.csdn.net/u/20080516/17/9b52e12a-0c87-4e35-98b8-d7a776b173bf.html
我问的 但是没有人回复
谢谢
比如:
string a = "123"; // 定义一个字符串,a里存的是"123"的地址
Test(a); // 调用方法,此时传递的也是a的地址,即传址,不是传值
void Test(string x){ // x得到了a的地址
x += "456"; // x重新赋值,此时,原地址指向的"123"不变,内存新开辟了一块内存,储存"123456",x指向"123456"的地址,但是x原来的那个地址的内容没变
}Console.WriteLine(a); // 因为a的内容没变,所以打出来还是123不知道你看明白了没有?
当把一个字符串变量富给另一个字符串时,会得到对内存忠同一个字符串两个引用,但是,如果修改其中一个字符串,就会创建一个全新的string对象,而另一个字符串没有改变。
总之,把string类型的对象大概的看成值类型操作就对啦!但是还是要记住他还是引用类型。
string s1 = "aaa";
string s2 = s1;
s1= "000";
s2 =? 又做了测试,增加了查看地址的代码:
protected void Test001(object sender, EventArgs e)
{
string s = "asd"; GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
Response.Write("传入前:" + handle.AddrOfPinnedObject().ToString() + "<br/>"); ff(s); handle = GCHandle.Alloc(s, GCHandleType.Pinned);
Response.Write("传入后:" + handle.AddrOfPinnedObject().ToString() + "<br/>"); Response.Write(s);
}
protected void ff(string s)
{
GCHandle handle = GCHandle.Alloc(s,GCHandleType.Pinned);
Response.Write("传入后修改前:" + handle.AddrOfPinnedObject().ToString() + "<br/>");
s += "1000";
handle = GCHandle.Alloc(s, GCHandleType.Pinned);
Response.Write("传入后修改后:" + handle.AddrOfPinnedObject().ToString() + "<br/>");
}结果是:
传入前:111311428
传入后修改前:111311428
传入后修改后:52077404
传入后:111311428可以看到传入之后地址没有改变,但是修改后s的地址改变了. 我是这样理解的,s只是一个表示,表示一个地址,应该不是指针的那个概念.可以直接看成一个地址. 在赋值之后,s代表了另外的一个字符串,但是并没有修改外面的s代表的地址.所以退出之后,传入之前的值没有改变.期待有更好的解释.
这个s的地址当然变了,不过这个s是ff这个方法的形参,它指向了一个新开辟的内存单元
而在调用ff方法之前,Test001里的那个s,已经压入堆栈了,根本没有参与ff方法不知道你还有什么不明白的。