为什么引用类型string的行为却是值类型

来源:互联网 发布:删除数据库 mysql 编辑:程序博客网 时间:2024/06/08 02:31
首先,我们需要知道string的如下特性: 

1.字符串做参数,传递的是引用,只是一个指针的复制,你修改参数的指向,影响不到调用它的函数中的那个指针所指向的内容
2.字符串不可修改。你以任何一种形式修改了字符串(+,=等操作),源字符串其实还在的,只是根据你的修改重新生成了一个新的字符串
3.如果需要修改,两种方案,一是将修改后的字符串返回;二是作参数传字符串时使用 ref 传递,这样子函数中可以直接修改掉主函数中的指针指向,从而指向新的字符串。
4  定义字符串时,会先查CLR内部的散列表。

 

  字符串类型就是一个值类型的引用类型,用c++的概念来解释的话,就是一个指针,即*pstr.
  
  *pstr = "123"; ----string中存放的是地址
  
  但是c#中做了特殊的处理,就是第2条,不能对修改pstr所指向的地址的值。当你修改*pstr的值时,其实c#并没有修改pstr所
  指的内存的值,而是重新分配了一块内存赋新的值,然后再将这块新的内存的首地址赋值给指针pstr. 正是因为这条奇怪的规  则,使得c#中的引用类型的string的行为却是值类型。

 
  对于第1条,字符串做参数时,其实就相当于c++中的指针做参数,*pstr,这样传递的都是地址。但是因为第2条的原则规则,函数内用传入的指针*pstr_input去修改值后,pstr_input指向了修改后的值的内存,而原始指针pstr还是指向原来的内存,pstr指向的内存的值和pstr_input指向的内存的值当然就不一样了。

 
  对于第3条, 当用 ref string 来传递参数时,其实就是相当于c++中的 **point指针的指针的方式了,这样传递参数的时候,参数中就是原始参数*pstr指针pstr的指针,即传递的值就是&pstr. 这样在函数中修改string时就会修改string的引用值,即修改*point的值。因为是用**point这样的指针的指针的方式,函数中的对*point的修改时可以带出去的。即函数中的修改,等于需改了主函数中的指针指向,函数中与主函数中都同时指向新的字符串。
 
  对于第4条,
string   str1="abc";
string   str2="abc";
当CLR初始化时,会创建一个内部的散列表,其中的键为字符串,值为指向托管堆中字符串的引用。刚开始,散列表为空,JIT编译器编译方法时,会在散列表中查找每一个文本常量字符串,首先会查找"abc"字符串,并且因为没有找到,编译器会在托管堆中构造一个新的指向"abc"的String对象引用,然后将"abc"字符串和指向该对象的引用添加到散列表中。
  接着,在散列表中查找第二个"abc",这一次由于找到了该字符串,所以编译器不会执行任何操作,代码中再没有其它的文本常量字符串,编译器的任务完成,代码开始执行。执行时,CLR发现第一个语句需要一个"abc"字符串引用,于是,CLR会在内部的散列表中查找"abc",并且会找到,这样指向先前创建的String对象的引用就被保存在变量s1中,执行第二条语句时,CLR会再一次在散列表中查找"abc",并且仍然会找到,指向同一个String对象的引用会被保存在变量s2中,到此s1和s2指向了同一个引用,所以System.Object.Equals(s1,s2)就会返回true了。
  另外,C#中是不允许用new操作符创建String对象的,编译器会报错

 

原创粉丝点击