前段时间发现很多人对参数传递仍然存在很多误区,特在此发个贴,有异意的地方大家一起讨论。参数传递分值传递和引用传递两种,这在书上都有。
通常,在没有显式指出ref和out时都是值传递。说到传值,必须又得说说值类型和引用类型。
值类型存放在堆栈中,直接访问。如果有:int a=0;int b=a;就产生了两个对象。
引用类型需要在堆中显式分配,且不能直接访问,需要在堆栈中分配一个标识对象指向其地址。这个标识对象类似于值类型。
如果:
StringBuilder strb = new StringBuilder();
StringBuilder strb2 = strb;
则在堆中只有一个StringBuilder对象,只是堆栈中有两个标识对象指向它。现在用例子来说明其不同:private void button2_Click(object sender, System.EventArgs e)
{
  StringBuilder strb1 = new StringBuilder();
  StringBuilder strb2 = new StringBuilder();
  Test1(strb1);
  Test2(ref strb2);
  string str1 = strb1.ToString(); //str1值:"A"
  string str2 = strb2.ToString(); //str2值:"BC"
}void Test1(StringBuilder strb)
{
  //strb和strb1是堆栈中的两个标识对象,但指向相同的地址,这个操作是改变堆中的对象。
  strb.Append("A");  //这里将strb指向一个新的堆中对象地址,所以后面的操作与strb1无关。这个新对象随着strb在堆栈中的消失最终成为垃圾对象
  strb = new StringBuilder("B");
  strb.Append("C");
}void Test2(ref StringBuilder strb)
{
  //这里的strb和strb2在堆栈中的地址是一样,即他们在堆栈中也是同一个标识对象。
  //那么不管对strb如何操作,同样都会反应到str2中。
  strb = new StringBuilder("B");
  strb.Append("C");
}值传递:传的是对象的值拷贝。(引用类型和值类型看上去不同,实质上一样,只是引用类型是拷贝堆栈中的标识对象,所以就是:两个标识对象,指向同一堆中的对象)
引用传递:传的是地址,即对象与函数参数对象完完全全是同一对象。(这里的对象都是指的堆栈中的对象,当然,堆栈中的标识对象是同一个,那么指向的对象就更是同一个,呵)
[Java]中没有引用传递。
[共享1]try{}finally{}中的return顺序问题:
http://community.csdn.net/Expert/topic/3990/3990402.xml

解决方案 »

  1.   

    靠。经典,一语中的,好贴。我刚转到C#,你这个解释最让人明白。我一开始还以为C#又开辟了一个特定内存空间呢,faint.收藏收藏
      

  2.   

    我也来凑凑热闹这里的堆是托管堆,对托管堆的维护是 CLR 最重要的工作之一。如果对托管堆中某一对象的所有引用全都失效(即,引用它的所有变量失效),该对象就标为“垃圾”(当然垃圾也有复活的可能,这里不谈)。以前这样的“垃圾”是要手工delete 的,如果引用非常多,delete 的时机就不太容易计算,如果忘了delete,该对象就会一直留在内存中,就是所谓的“内存泄露”。现在 CLR 的垃圾回收帮我们做了这个工作
      

  3.   

    [共享3]C#的字符编码的理解,迷惑了我很久,希望会对一部分同样迷惑的人有所帮助(有错误的地方麻烦指出)误区:
    1.入门C#时经常看到这样的描述:.NET中的String都是Unicode编码。
    在入门之后没太看这样的基础书并且多接触一些编码问题后,我的潜意识总觉得String有很多种编码,utf8,unicode,ascii等,并且不认为C#中有gb2312编码。
    2.System.Text.Encoding.Default似乎可以解决一切编码,因为我每次用Default.GetString()来读取流中的字符串都成功!所以Default应该是根据字节的编码方式而改变的,比如如果你的字节序是ascii编码,那么Default就是ascii编码。只到前几天在CSDN的Java社区看到一个编码问题,外加自己的几个小时的实验,终于对编码问题理清了头绪。简短描述:
    .NET中的String确实只有Unicode一种。所以编码格式的字节序列转换成String时最终都是以Unicode表示。转换成String后它以后的编码格式已经不重要或者说没有意义了。
    System.Text.Encoding.Default是取系统的当前ANSI代码页的编码(MSDN上抄的),即当前系统的编码。(在我们的机子上一般都是"gb2312")这就是我每次用Default读取文件流都正确,且必须用Default读取才正确的原因----其实用Encoding.GetEncoding("GB2312")也一样。详细介绍:
    在每次进行byte[]-->String(other-->Unicode)和String-->byte[](Unicode-->other)时都会有编码转换。
    比如通常的转换都有设置编码的地方,如StreamReader(string path [, System.Text.Encoding encoding]),Response.Charset,这就相当于你告诉系统byte[]是什么编码,这时候.NET用你指定的编码方式去解码,然后转换成Unicode编码方式的String.
    也就是说,不管何时,我们所指定的编码都只是指byte[]。
    即Encoding.UTF8.GetString(byte[] buffer)是告诉系统buffer的编码是UTF8。
    byte[] buf = Encoding.UTF8.GetBytes(string str)是告诉系统返回的buf编码方式是UTF8。你可能告诉系统一个假的编码方式,或者你没有告诉系统并且byte[]的编码不是用的默认编码,那么系统解码仍然会用指定编码方式进行,在机器看来他仍然解码成功,然后转换成Unicode编码,因为机器只知道0,1字节序,他不知道解码出来的东西是否是混乱的,在他看来一切正常。
    但我们去看,就会发现字符串成了一些莫名其妙的符号而没有任何意义,这就是所谓的乱码。
    就好比:"you yi ge",本来是拼英"有一个",你却偏偏告诉别人这是英语,别人用英语去拼,就不知道是怎么回事了,就成了乱码,呵呵。
    即:字节序是按指定编码方式编码,它有一个特定的编码方式,但它本身是中性的,不含有任何编码信息。编码方式理论上是独立于语言的,但实际上需要语言去支持。如JAVA中有"GBK"(gb2312扩展)编码,但.NET中没有。你用Encoding.GetEncoding("GBK")会抛异常。
      

  4.   

    补上这个:ref和out的区别:其实这个是用于让编译器检查值类型的变量是否被赋初值的。
    ref要求变量必须在传递之前赋值,所以如下语句是错误的:int i;
    FunctionA( ref i );
    out不要求变量在传递之前赋值,但对接收变量的函数进行了约束,要求在该函数内必须给变量赋值。
    简而言之就是,如果编译器在函数声明中看到了ref,则认为这个变量是已经被赋了初值的,而如果在声明中看到了out,则认为这个变量没有被赋初值,而不论其是否在前面已被赋初值。如:void FunctionA( out int i )
    {
      int j = i;//错误,编译器会说i没有被赋初值。
    }除此之外,标明为out的参数必须在函数体内部进行赋值,并且是所有的流程途径都必须有明确的赋值。