C#字符串

【主要内容】

关于C#的String。
C# String 和 string 有何区别
字符串的不变性
字符串比较 == 和 Equals(string str1, string str2)
字符串驻留

【概念阐述】
====================
@ C# String 和 string 有何区别
====================
1、string是c#中的关键字,String是.net Framework的类;
2、c# string映射为.net Framework的String,如果用string,编译器会把它编译成String,所以如果直接用String就可以让编译器少做一点点工作;
3、string始终代表System.String,String只有在前面有using System的时候并且当前命名空间中没有名为String的类型(class、struct、delegate、enum)
     的时候才代表System.String

====================
@字符串的不变性
====================
1、一个字符串一旦被创建完成后,将不能再做任何改变,比如改变、增加或减少一个字符。
2、当把一个字符串变量赋给另一个字符串变量时,会得到内存中同一个字符串的两个引用,但与引用类型在常见的操作上又有区别。当你修改其中一个字符串的时候,
     会创建一个全新的String对象,其引用的原字符串并不会改变,因而另一个字符串变量的值不变。
3、当我们创建了字符串对象a,它的值是“1234”,当我们再创建一个值为“1234”的字符串对象b时它不会再去分配一块内存空间,而是直接指向了a在内存中的地址。

=================================
@字符串比较 == 和 Equals(string str1, string str2)
=================================
==是Equals(string,string)的一个重载,两者内部机制完全一样。如果两字符串引用相同直接返回true,若不同则先比较引用再比较字符串的值。

public   static   bool   operator   == ( string  a,  string  b){
      
return   string .Equals(a, b);
}

public   static   bool  Equals( string  a,  string  b){
      
if  (( object )a  ==  ( object )b){   //  先比较引用是否相同,若相同直接返回True。
             return   true ;
      }
      
if  ((a  !=   null &&  (b  !=   null )) {  //  引用不同的再比较值是否相同。 
             return  a.Equals(b);
     }
     
return   false ;
}

【实验步骤】
=========================
@不可使用new操作符来创建字符串对象
=========================

public   class  CreateString{
 
public   static   void  Main( string [] args){
  
string  str  =   new   string ( " a string " );
 }
}

编译错误提示:
error CS1502: 与“string.String(char*)”最匹配的重载方法具有一些无效参数
error CS1503: 参数“1”: 无法从“string”转换为“char*”

事实上 string 类并未提供 string(string) 构造函数。

==================
@字符串连接测试(不变性)
==================

字符串连接
using  System;

public   class  StringTest{
 
public   static   void  Main( string [] args){
  
//  String 为引用类型,但却有一些值类型的表现。
   string  s1  =   " a string " ;
  
string  s2  =  s1;      //  s1 和 s2 都引用 "a string"。
  Console.WriteLine( " s1 is  "   +  s1);
  Console.WriteLine(
" s2 is  "   +  s2); 
 
  s1 
=   " another string " ;    //  修改 s1。
  Console.WriteLine( " s1 now is  "   +  s1);
  Console.WriteLine(
" s2 now is  "   +  s2);   //  s2 的值仍为 "a string"。
  
  Console.ReadLine();
 }
}

输出:
s1 is a string
s2 is a string
s1 now is another string
s2 now is a string

=============
@ 字符串存储
=============
1、相同字符串在内存中只保存一份。

using  System;
class  Test{
        
static   void  Main( string [] args){
            
string  a  =   " 1 " ;
            
string  b  =   " 1 " ;
            
string  c  =   " 2 " ;
        }
    }
}

使用visual studio 2005中的调试,转到反汇编,可以得到下面内容:
......
            string a = "1";
0000002b  mov         eax,dword ptr ds:[022C3048h] // 与下面相同
00000031  mov         ebx,eax
            string b = "1";
00000033  mov         eax,dword ptr ds:[022C3048h] // 与上面相同
00000039  mov         esi,eax
            string c = "2";
0000003b  mov         eax,dword ptr ds:[022C307Ch] // 不同
......

2、相同字符串GetHashCode()返回相同值。

using  System;
public   class  Test{
 
public   static   void  Main( string [] args){
  
string  a  =   " 0 " ;
  
string  b  =   " 0 " ;
  
string  c  =   " 1 " ;
  Console.WriteLine(a.GetHashCode());
  Console.WriteLine(b.GetHashCode());
  Console.WriteLine(c.GetHashCode());
 }
}

输出:
-842352752
-842352752
-842352753

3、字符串驻留

using  System;
public   class  Test{
 
public   static   void  Main( string [] args){
  String s 
=  “Hello”;
  Console.WriteLine(Object.ReferenceEquals(“Hello“,s));
 }
}

输出:
True

说明:当 CLR 初始化时,它会创建一个内部的散列表,其中键位字符串,值为指向托管堆中字符串对象的引用,初始化为空。当JIT编译器编译方法时,
它会在散列表中查找每一个常量字符串,并添加到散列表中,值相同的字符串不重复添加。对于上面的代码,编译器会对查找到的第一个"Hello"字符串,
将现在托管堆中构造一个新的 String 对象(指向在字符串),然后将 "Hello" 字符串和指向该对象的引用添加到散列表中。由于散列表中已经存在 "Hello"
字符串,对于查找到的第二个 "Hello" 字符串,将不执行任何操作。
    代码执行时,它会在第一行发现一个 "Hello" 字符串引用,于是便在内部散列表中查找 "Hello",找到后将先前创建的 String 对象的引用保存到变量 s 中。
当执行第二行代码时,CLR 会再一次在内部散列表中查找 "Hello",并把对应的 String 对象的引用传递给 Object 的静态方法 ReferenceEquals 作为参数。
    通过上面的分析,显然结果为 True。当一个引用字符串的方法被 JIT 编译时,所有嵌入在源代码中的常量字符串总会被添加到 CLR 内部的散列表中,
但是,运行时动态创建的字符串却不会,参看下面的代码:

using  System;
public   class  Test{
 
public   static   void  Main( string [] args){
  
string  s1  =   " Hello " ;
  
string  s2  =   " Hel " ;
  
string  s3  =  s2  +   " lo " ;

  Console.WriteLine(Object.ReferenceEquals(s1,s3));  
 }
}

输出:
False

说明:在上面的代码中,s2 引用的字符串 "Hel" 和一个文本常量字符串 "lo" 连接构造一个新的位于托管堆中的字符串对象,该引用保存在 s3 中,但并未添加到散列表中,
与散列表中的 "Hello" 在托管堆中各有一份存储,因此返回的结果是 False。需要注意的是,如果 s3 连接的是两个字符串常量 "Hel" + "lo",则又将返回 True。这是因为
C# 编译器在将代码编译成 IL 指令时会将两者连接。

============
@字符串比较
============

using  System;
public   class  Test{
 
public   static   void  Main( string [] args){
  
string  s1  =   " Hello " ;
  
string  s2  =   " Hel " ;
  
string  s3  =  s2  +   " lo " ;

  Console.WriteLine(Object.ReferenceEquals(s1,s3));
  Console.WriteLine(Equals(s1,s3));
  Console.WriteLine(s1 
==  s3);
 }
}

输出:
True
True

说明:参看概念阐述中对字符串比较内部实现的说明。虽然 s1 和 s3 引用不同,但String 的 Equals(string,string) 方法还会对字符串的值进行比较,因为值相同,所以返回结果为 True。显然用 Equals(string,string)/== 效率上要比仅比较引用的 ReferenceEquals(string,string) 差很多,如果应用程序中所有的字符串比较都仅比较引用,性能将会大大提升,String 类提供的两个静态方法允许我们做到这一点。

public   class  Test{
 
public   static   void  Main( string [] args){
  
string  s1  =   " Hello " ;
  
string  s2  =   " Hel " ;
  
string  s3  =  s2  +   " lo " ;

  s3 
=  String.Intern(s3);

  Console.WriteLine(Object.ReferenceEquals(s1,s3));
  Console.WriteLine(Equals(s1,s3));
  Console.WriteLine(s1 
==  s3);
 }
}

输出:
True
True
True

说明:方法 Inter(string) 在 CLR 内部散列表中查找参数指定的字符串,如果能找到返回其引用,如果未找到,字符串将被添加到散列表中,并返回引其引用。
如果 String 对象不再被进程中的所有应用程序域所引用(作为参数传递给 Intern 方法的那个 String 对象),垃圾收集器可以收回其所占内存及在三列表中的记录。
一个字符串对象可以被同一个进程中的多个应用程序域访问,即字符串的驻留是以进程为单位的。参看下面的代码:

using  System;
public   class  CreateString{
 
public   static   void  Main( string [] args){
  
string  s1  =   " Hello " ;
  
string  s2  =  s1;
  
string  s3  =  s1  +   "  joe " ;

  
// MyIntern();

  Console.WriteLine(String.IsInterned(s1) 
!=   null );
  Console.WriteLine(String.IsInterned(s2) 
!=   null );
  Console.WriteLine(String.IsInterned(s3) 
!=   null );
 }

 
public   static   void  MyIntern(){  
  String.Intern(
" Hello joe " );
 }
}

输出:
True
True
False

如果将注释部分加入到代码中则输出:
True
True
True

【参考资源】
《你真的了解.net中的String吗》 http://terrylee.cnblogs.com/archive/2005/12/26/304876.aspx
《进一步了解 String》  http://lixianhuei.cnblogs.com/archive/2005/12/27/305445.html
《Microsoft.NET框架程序设计》

 

转载于:https://www.cnblogs.com/JoeDZ/archive/2008/06/27/1230857.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值