.Net 框架程序
1 认识string
字符串是我们编程中使用最多的类型之一,在C#语言中它作为基元类型。从继承性上来说,string直接继承自object,它是一个引用类型,总是分配在托管堆中,但是它的行为表现的却有着值类型的特性,比如它可以不用new去构建对象,而可以直接赋值的方式;作为函数的引用类型参数,在函数内的操作不会影响到形参等等。
[csharp]
class Program
{
static void Main(string[] args)
{
public string s = "hello";
Printing (s);
Console.WriteLine(s);
}
}
public static void Printing(string ss)
{
ss = "hello world";
Console.WriteLine(ss);
}
在上面的代码中声明了一个字符串变量s,对变量直接进行了赋值s = "hello",而没有使用创建引用类型的方式s= new string("hello"),我们用ILDasm.exe去查看生成的IL代码,会发现在创建引用类型对象时使用的newobj并没有出现,取而代之的是一个新指令ldstr,它从元数据中获取一个文本常量来构建一个string对象,CLR通过这种方式来优化对string类型的操作。紧接着Printing函数会被调用,它接收一个string对象,按照我们对引用类型变量的理解,Printing函数在内部对string对象的修改会影响外部的string对象,但运行结果并没有像我们想的那样,外部的对象并没有发生变化,而这正好印证了上面所说的string通过特殊的语法去创建对象。在调用Printing函数时如下示意图所示:在Printing函数内部发生了如下的变化:
通过对上面的示意图我们不难理解函数的输出。
2 字符串恒定性
字符串是恒定的,在创建后就不会在变化,这是string一个很重要的特性。考虑如下代码:
string s ="hello world";
Console.WriteLine(s.ToUpper().Substring(1,2));
Console.WriteLine(s);
在输出的第一行我们对字符串做了一些操作,但是这些操作并没有更改s的值,在第二行的输出中仍然是hello world。在对s的操作中,每次的操作都会创建一个新的string对象,原有的s并没有改变,这就是字符串的恒定性。
这个特性为string类型带来了一些好处同时也带来了一些性能上的损失,好处是在不用锁定的情况下就可以在多个线程间操作而不用同步;坏处是对字符串的操作产生了大量的临时对象,这些对象会加剧垃圾收集器的收集工作,对性能有一定的影响。
3 字符串的驻留
因为字符串的恒定性,在作为函数参数或其他一些拷贝操作时都会产生一个全新的副本,这对内存是一个极大的浪费;同时字符串的比较也是一个费时的操作,它要比较字符串中对应位置的每一个字符。为了解决上述的问题,CLR在内部维护一个哈希表,将字符串作为键,将该字符串在托管堆中的引用作为值。当创建一个字符串的时候,CLR会首先在这个哈希表中去查找对应的键是否存在,如果不存在,就创建一个该字符串的副本,把该副本存储在哈希表中,并返回一个该副本的引用;如果存在和创建的字符串相同的键,则直接返回该键对应的值(字符串的引用)。
保存在哈希表中的字符串不受垃圾收集器的管理,也就是说只有在程序域被卸载的时候,这个哈希表才会被释放。
对动态创建的字符串,CLR并没有选择留用,我们可以用string的静态方法Intern来实现强制的留用。如下的代码:
[csharp]
string s1 = "hello";
string s2 = "hello";
bool b = object.ReferenceEquals(s1, s2);
Console.WriteLine("s1和s2是否具有相同的引用:"+b);
string s3 = "hello world";
string s4 = s1 + " world";
bool b1 = object.ReferenceEquals(s3, s4);
Console.WriteLine("s3和s4是否具有相同的引用:" + b1);
Console.WriteLine("s3和s4是否具有相同的值:" + s3.Equals(s4));
string s5 = string.Intern(s4);
b1 = object.ReferenceEquals(s3, s5);
Console.WriteLine("s3和s5是否具有相同的引用:" + b1);
输出如下:
1和s2是否具有相同的引用:True
s3和s4是否具有相同的引用:False
s3和s4是否具有相同的值:True
s3和s5是否具有相同的引用:True
4 字符串池
编译源代码时,文本常量会被嵌入到元数据中,这样就会导致相同的字符串会被多次的嵌入到元数据中,增大了生成模块的容量。为了解决这个问题,C#编译器把字符串写入一次后,在后面的代码中出现相同的,则只是对写入字符串的一个引用,有效保证了生成模块的容量,这就是字符串池技术。
补充:Web开发 , ASP.Net ,