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时,传入的是什么呢

解决方案 »

  1.   

    因为string str = "123";
    str = "234" //这是str已经指向了另一个新的字符串,而不是把原来的字符串从123改为234
      

  2.   

    我明白你说的是什么意思,但是我觉得string我传入的是一个地址。
    引用类型是在堆栈里放一个地址,而在托管堆里存值,我传参传入的是一个堆栈里的地址,
    string str = "123";
    str = "234" //这是str已经指向了另一个新的字符串,而不是把原来的字符串从123改为234
    我明白是另外一个地址,前一个地址废弃了等待gc回收,但是这个在托管堆里的内存它应该是和传入的那个地址对应的,也就是传入的那个地址不再指向123 所在的地址,而是指向了234所在的地址,
    不知道自己说的对不对。
    高手指教
      

  3.   

    string 有其特殊性,private void ChangeReferenceValue(string inValue)
    {
                inValue = "456";

    这个函数调用大致是这样的
    invalue 这个地址压栈
    call ChangeReferenceValue
    inValue 这个变量指向了新的地址 "456"
    end
    invalue 原先的地址出栈,覆盖了在函数中改变的地址 ,又指回了原来的"123";
      

  4.   

    类似public class class1
    {
    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 不变
      

  5.   

    string类型的虽然是引用类型的,但是它比较特殊,有值类型一样的操作方式,所以搂主得到的答案并不奇怪!
    当把一个字符串变量富给另一个字符串时,会得到对内存忠同一个字符串两个引用,但是,如果修改其中一个字符串,就会创建一个全新的string对象,而另一个字符串没有改变。
    总之,把string类型的对象大概的看成值类型操作就对啦!
      

  6.   

    看下string 类型的串 和 System.Text.StringBuilder 类型吧:就拿你举的列子我详细的说下
    你的程序;
    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 指向了别的地方, 原来的内存内容并没有被修改        } 
      

  7.   

    关于string是一个特殊的引用类型我觉得不合适,我又试了一个例子用的是类。
     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#默认是值类型传递,难道就是这个意思,任何改变都在函数里改变,进入函数后重新分配内存,当出这个函数后,这个地址依然指向进入函数前的那块内存?
      

  8.   

    没有你们想的那么复杂...在 C# 中,所有参数在默认情况下均通过值传递。若要通过引用传递,您需要指定关键字 ref 或 out。是只传递“值”...不管你是不是值类型...所以对引用类型来讲传递的那个“值”是一个完全的副本...
      

  9.   

    string的取值并不是取自一个引用地址所指向的内容 而是字符串池中的一个"常量" 所以在
    private void ChangeReferenceValue(string inValue) 

                inValue = "456"; 
    }
    中 并不是改变了原实参"123"的引用地址 而是新创建了(或者是在字符串池中取到了已经存在的)形参"456" 
    我是这样理解的
      

  10.   


    不会这样吧。如果真的是传入的一个完整的副本,一个简单的string类型还好,如果是一个大的自定义结构怎么办,难道也会在内存里重新分配一块内存出来?
      

  11.   

    误导,在 C# 中,所有值类型参数在默认情况下均通过值传递string是引用类型,但是当参数时默认情况下也是传递值
    这是string与其它引用类型不一样的地方。
      

  12.   

    楼上,如果按照你说的string是一个特殊的例子,但是请看9楼我写的例子,为什么我用一个类的int类型来写仍然是这个问题呢
      

  13.   

    你要确认,你Write的那个a和t的a是2个不同的变量,你可以改成下面这样试试
    Console.Write(a); 
    ChangeReferenceValue(this); 
    Console.Write(a); 
      

  14.   

    在 C# 中,所有参数在默认情况下均通过值传递。若要通过引用传递,您需要指定关键字 ref 或 out。  这个说法是不对的,虽然帮助也是这样写的.例子:
    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只是修改了引用的地址.原来地址中的内容不会被覆盖或者修改.
      

  15.   


    不好意思,是我弄错了,看上面的代码 我第一次输出的是构造函数里输出的值,但是紧接着我又new了一个对象,当我把这个new的对象传入函数时当然不会修改我构造函数里的值了。
    谢谢回复。
      

  16.   

    但是对这个string为什么特殊还是有一些不明白。
    看了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楼谢谢回复
      

  17.   

    如果方便看下这个问题
    http://topic.csdn.net/u/20080516/17/9b52e12a-0c87-4e35-98b8-d7a776b173bf.html
    我问的 但是没有人回复
    谢谢
      

  18.   

    其实,你这么理解,string是只读的,不可修改的,就对了
    比如:
    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不知道你看明白了没有?
      

  19.   

    string类型的虽然是引用类型的,但是它比较特殊,有值类型一样的操作方式,所以搂主得到的答案并不奇怪! 
    当把一个字符串变量富给另一个字符串时,会得到对内存忠同一个字符串两个引用,但是,如果修改其中一个字符串,就会创建一个全新的string对象,而另一个字符串没有改变。 
    总之,把string类型的对象大概的看成值类型操作就对啦!但是还是要记住他还是引用类型。
      

  20.   

    我不太认同退出的时候还原的说法, 这个说法无法解释下面的语句:
    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代表的地址.所以退出之后,传入之前的值没有改变.期待有更好的解释.
      

  21.   

    To楼上的:s += "1000"; 之后
    这个s的地址当然变了,不过这个s是ff这个方法的形参,它指向了一个新开辟的内存单元
    而在调用ff方法之前,Test001里的那个s,已经压入堆栈了,根本没有参与ff方法不知道你还有什么不明白的。