摘要:string是编程中使用最频繁的类型。一个string表示一个恒定不变的字符序列集合。string类型直接继承自object,故他是一个引用类型,也就是说线程的堆栈上不会有任何字符串(直接继承自object的类型一定是引用类型,因为所有的值类型都继承自System.ValueType。值得指出的是System.ValueType是引用类型)。

string是编程中使用最频繁的类型。一个string表示一个恒定不变的字符序列集合。string类型直接继承自object,故他是一个引用类型,也就是说线程的堆栈上不会有任何字符串(直接继承自object的类型一定是引用类型,因为所有的值类型都继承自System.ValueType。值得指出的是System.ValueType是引用类型)。

一、创建字符串

   C#将string认为是基元类型,也就是说编译器也许在源代码中用文本常量来直接表达字符串。编译器会将这些文本常量字符串存放在托管模块的元数据中。在C#中创建string。

  不能通过new操作符来创建string

 class Program
{
static void Main(string[] args)
{
string hap = new string("heaiping");
}
}

以上代码会出现编译错误。

相反,应该使用下面的简化语法

  class Program
{
static void Main(string[] args)
{
string hap ="heaiping";
}
}

为什么呢,请看下面代码。

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s = "heaiping";//不使用new
SomeClass sc = new SomeClass();//使用new
}
} class SomeClass
{
}
}

编译以上代码查看ILDasm生成的IL代码如下。

代码

.method private hidebysig static void  Main(string[] args) cil managed
{
.entrypoint
// 代码大小 14 (0xe)
.maxstack 1
.locals init ([0] string s,
[1] class StringStudy.SomeClass sc)
IL_0000: nop
IL_0001: ldstr "heaiping"//此处创建string
IL_0006: stloc.0
IL_0007: newobj instance void StringStudy.SomeClass::.ctor()//此处创建SomeClass
IL_000c: stloc.1
IL_000d: ret
} // end of method Program::Main

  如上所见C#对于对象实例的IL指令为newobj,然后调用其构造函数,而对于string,它用了一个特殊的指令ldstr(是不是loadstring,呵呵),该指令通过从元数据中获取文本常量来构造string对象,这表明,CLR有一个更为高效特殊的字符串构造方式。

  string类型也通过了通过new来构造而不是通过文本元数据来构造的构造器,可以接受char*等参数,书上说是为了托管C++提供的,这个就不研究了~~~~~~~

  看下面的代码

  class Program
{
static void Main(string[] args)
{
string s = "heaiping";
s = s + " " + "soft";
}
}

  上面的代码使用了+操作符来连接字符串,书上建议不要使用+,因为它会在要执行垃圾回收器的托管堆上创建多个字符串,不是单纯的附在后面。应该使用System.Text.StringBuilder(下次学习);

  还有一种特殊的声明方式,允许编译器将引号之间的字符都认为是字符串的一部分。

代码

   class Program
{
static void Main(string[] args)
{
//fileA和fileB是一样的
string fileA = "C://Windows//Temp";
string fileB = @"C:/Windows/Temp";
}
}

@符号会爆速编译器该字符串为一个字面字符串,告知编译器将反斜杠看为字符而不是转义字符。

二、字符串的恒定性

  字符串的恒定性,就是说,一个字符串一旦被创建,就不可能将其变长、变短、或者改变其中任何的字符。

三、字符串比较

罗列一下

成员 成员类型 描述
Compare 静态方法 按照字符串的排序比较,可以控制语言文化,以及是否考虑大小写
CompareTo 实例方法 按照字符串的排序比较,内部使用了当前线程的语言文化
StartsWith/EndsWith 实例方法 是否以指定的字符串开头或者结尾,大小写敏感,内部使用了当前线程的语言文化
CompareOrdinal 静态方法 按照字符集比较,不考虑语言文化,大小写敏感,比较快
Eauals 静态/实例 静态方法比较字符集,实例方法内部调用CompareOrdinal,静态方法首先会检查两个引用是否指向同一个对象,如果是则不再比较字符集
GetHashCode 实例方法 返回列散码

 除以上,string类型还承载了==和!=操作符,其方法内部都是调用string的静态Equals方法实现的 

 对于以上比较有以下说明:

  • 如果是判断字符串是否相等,用CompareOridinal,因为它仅仅是比较字符集是否相等,比较快。
  • 如果是逻辑比较,呈现给用户,何为逻辑比较,虽然有些字符串的字符集不一样,但是逻辑上确实是等的,应该使用Compare方法,Compare内部使用了特定语言文化的排序表。

  在字符串的比较时,语言文化对其影响是很大,Compare方法在内部首先获取与调用线程相关的CurrentCulture,然后读取CurrentCulture的CompareInfo属性。因为CompareInfo对象封装了一个和语言文化相关联的字符比较表,所以每一种文化仅有一个CompareInfo对象。

四、字符串驻留

  字符床的比较是很浪费性能的,CLR通过字符串驻留(string interning)机制来提高性能,看下面代码:

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s = "hap";
Console.WriteLine(object.ReferenceEquals("hap",s)); SomeClass sc = new SomeClass();
Console.WriteLine(object.ReferenceEquals(new SomeClass(), sc));
}
} class SomeClass
{ }
}

按照引用类型的特性,上面的两个输出都应该为False,因为都是不同的引用在比较,可是  Console.WriteLine(object.ReferenceEquals("hap",s));却输出为True,为什么,难道string不是引用类型吗?? string当然是引用类型,上面的表现由CLR对于string的特殊处理决定的:

  当CLR初始化的时候,它会创建一个内部的散列表,键为字符串,值为指向托管堆中字符串对象的引用。刚开始,该表为空。当JIT编译器编译方法时,它会在散列表中查找每一个文本常量字符串。对于上面的关于string的代码,编译器会首先查找“hap”字符串,并且因为没有找到(第一次还没有),它便就在托管堆上面构造一个新的string对象(指向hap),然后将hap字符串和指向该对象的引用添加到散列表(此时散列表中已经存在键为hap的值),接着JIT编译器在编译器中查找第二个hap字符串,当然此时散列表中已经存在hap,所以不会执行任何操作,代码开始执行。

  当代码执行时,它会在第一行发现需要hap字符串的引用。于是,CLR便在其内部的散列表中查找hap,并且会找到它,这样指向先前创建的string对象的引用就被保存在变量s中。当再往下执行,CLR会再一次在其内部散列表中查找hap,并且仍然会找到,这样子,指向同一个string对象的引用会被传递给object的ReferenceEquals作为第一个参数,当然和第二个参数是同一个引用,自然比较结果为True。

  继续看下面代码:

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s1 = "hap";
string s2 = "h";
string s3 = s2 + "ap";
string s4 = "h" + "ap";
Console.WriteLine(object.ReferenceEquals(s1,s3));//false
Console.WriteLine(s1.Equals(s3));//true Console.WriteLine(object.ReferenceEquals(s1, s4));//true }
}
}

这次的输出搞的怪怪的,心里有点毛,到底是怎么回事?原来当一个引用字符串的方法被JIT编译时,所有嵌入在源代码中的文本常量总会被添加到散列表中,s2引用的字符串(h)和一个文本常量字符串(ap)被连接在一起。结果是一个新构造的、位于托管堆中有s3引用的字符串对象,这个动态创建的字符串包含的是一个hap,但是它没有被添加到CLR散列表中,而是另起炉灶,不同的引用所有ReferenceEquals返回fasle,调用Equals相等是因为他们有相同的字符集。

  至于s4=”h"+"ap",是因为IL指令会将两个文本常量字符串连接为一个文本常量字符串hap,所有输出为True。

  ReferenceEquals方法在比较时不需要逐个字符的去比较,只比较引用,效率明显要高于Equals,如果将程序中所有的字符串比较都用引用而不是字符集,那么系统的性能将得到很大的提高。如果有一个方法要是能将含有相同字符集的动态字符串变为托管堆中的一个字符串对象的话,应用程序需要的对象也将更少,从而可以提升系统性能。非常幸运。string类型提高了两个静态方法将可以做到这一点。

public static string Intern(string str);
public static string IsInterned(string str);

  第一个方法Intern接受一个string参数,然后在CLR内部散列表中查找它。如果能够找到该字符串,Intern将返回已经存在的引用。如果找不到,该字符串将被添加到散列表中,Intern最后也会返回它的引用。如果引用程序不再保存作为参数传递的string的引用,垃圾回收器将会回收它。对上面的程序用Intern重新修改:

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s1 = "hap";
string s2 = "h";
string s3 = s2 + "ap";
s3 = string.Intern(s3);
Console.WriteLine(object.ReferenceEquals(s1,s3));//true
Console.WriteLine(s1.Equals(s3));//true
}
}
}

输出全部为true,神奇啊。因为Intern也是需要执行时间的,所以书上建议只有需要多次比较同一个字符串时,才 应该使用字符串驻留技术,否则得不偿失。

  需要注意的是,垃圾回收器不会释放CLR内部散列表中引用的字符串对象。只有当应用程序域都不再应用这些字符串对象时,他们才会被释放。

  IsInterned方法与Intern方法的不同之处是如果在散列表中找不到将会返回null,而不是创建。

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s1 = "heaiping";
string s2 = "h";
string s3 = s2 + "ap";
s3 = string.IsInterned(s3);
if (s3 == null)
{
Console.WriteLine(0);
}
else
{
Console.WriteLine(1);
} }
}
}

上面程序输出0,因为就当前程序来说散列表中只存在heaiping而不存在hap,所以最后s3变为null。

string还有一些成员Length,IndexOF...Copy...等等。。。不再说了,累了啊。

  

对了以前发过一个关于string的小组讨论http://home.cnblogs.com/group/topic/38270.html

原文如下:

class Class1 

    static void StrChange(string str) 
    { 
      str = "hellow"; 
    }

static void Main() 
    { 
      string str = "123";//申明一个字符串 
      StrChange(str);//调用方法 
      Console.WriteLine(str);//输出字符串 
    } 
}

输出的结果是 "123" 
string 到底是值类型还是引用类型? 
如果是值类型,结果倒还说的过去.但是我记得string 是引用类型啊...难道是我记错了?? 
如果是引用类型的话.输出的结果应该是: "hellow" 
请问这是为什么啊?? 大家帮忙解释一下..谢谢

现在明白了,其实就是与CLR对于string的这个散列表有关,

至于string是特殊的引用类型,个人感觉特殊就特殊在这个散列表上吧

C#文本处理(String)学习笔记的更多相关文章

  1. Java的string学习笔记 与char数组和bufferstring的比较

    ---恢复内容开始--- 一直用的C 导致这种类望而生畏 现在终于鼓起勇气学习一下 首先学习string类型 String s1 = "AbCdEf"; String s2 = & ...

  2. zepto源码--核心方法5(文本操作)--学习笔记

    涉及到文本内容的主要有三个函数:html, text, val. 我们已经见过多次,一个函数多种用途的情况,今天这三个函数也不例外,既可以获取内容,也可以设置内容.判断条件就是有没有传入参数,如果没有 ...

  3. Java String学习笔记

    参照:https://www.jianshu.com/p/2f209af80f84 常量池: Java代码被编译成class文件时,会生成一个常量池(Constant pool)的数据结构,用以保存字 ...

  4. Python学习笔记基础篇——总览

    Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...

  5. Linux学习笔记(四) vi编辑器

    一.vi 编辑器 vi 编辑器 (Visual Interface) 是所有 Unix 及 Linux 系统下标准的编辑器,相当于 Windows 系统中的记事本 它有三种模式,分别是: Comman ...

  6. Python 3之str类型、string模块学习笔记

    Windows 10家庭中文版,Python 3.6.4, Python 3.7官文: Text Sequence Type — str string — Common string operatio ...

  7. canvas学习笔记(中篇) -- canvas入门教程-- 颜色/透明度/渐变色/线宽/线条样式/虚线/文本/阴影/图片/像素处理

    [中篇] -- 建议学习时间4小时  课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...

  8. 机器学习框架ML.NET学习笔记【3】文本特征分析

    一.要解决的问题 问题:常常一些单位或组织召开会议时需要录入会议记录,我们需要通过机器学习对用户输入的文本内容进行自动评判,合格或不合格.(同样的问题还类似垃圾短信检测.工作日志质量分析等.) 处理思 ...

  9. Flutter学习笔记(11)--文本组件、图标及按钮组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 文本组件 文本组件(text)负责显示文本和定义显示样式,下表为text常见属性 Text组件属性及描述 属性名 类型 默认 ...

  10. JavaSE学习笔记(5)---内部类和String类

    JavaSE学习笔记(5)---内部类和String类 一.内部类基础 转自菜鸟教程 ​ 在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来 ...

随机推荐

  1. Linq语法

    希望能帮助一些linq新手. 开门见山 读这篇文章之前,我先说下,每一种搜索结果集,我都以三种方式变现出来,为啦更好的理解,希望不要嫌我啰嗦. 1.简单的linq语法 //1 var ss = fro ...

  2. 关于ligerui 中 grid 表格的扩展搜索功能在远程数据加载时无法使用的解决办法

    要想使用grid里的扩展搜索功能,除了要引用ligerui主要的js文件外,还必须引入下面的JS文件: 1.Source\demos\filter\ligerGrid.showFilter.js 2. ...

  3. 页面嵌套 Iframe 产生缓存导致页面数据不刷新问题

    最近遇到个比较古怪的问题:当页面嵌套多个 Iframe 时会出现 Iframe 里包含的页面无法看到最新的页面信息. 初步解决方案,在 Iframe 指向的页面地址后缀添加一个随机数或者时间戳.这样能 ...

  4. ASP.NET MVC5总结(二)@HTML扩展

    1.@Html.AntiForgeryToken() 用来防止跨站请求伪造(CSRF)攻击的一个措施 2.@Html.ValidationSummary(true) 主要用来 (1). 显示后台 Mo ...

  5. IOS中的NSTimer定时器详解

    /* 在IOS中有多种定时器,这里我对NSTimer定时器做了一个简单的介绍.如果你是小白,你可能会从这篇文章中学习到一些知识,如果你是大牛,请别吝啬你的评论,指出我的不足,你的质疑是对我最大的帮助. ...

  6. JavaScript—赋值表达式-1

    赋值表达式的运算顺序是从右到左的,因此,可以通过以下方法对多个变量赋值 i=j=k=0;//也就是把三个变量初始化为0 赋值表达式中的递增和递减 n++和++n的区别: 简单来说,根据运算顺序,n++ ...

  7. 九度OJ 1078 二叉树遍历

    题目地址:http://ac.jobdu.com/problem.php?pid=1078 题目描述: 二叉树的前序.中序.后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历 ...

  8. ORACLE数据库闪回日志写满

    网站页面无法显示完整.检查web服务是正常的,所以可能是ORACLE数据库出了问题. 首先检查闪回日志写满 然后检查归档日志文件写满的缘故了.使用以下几个命令可以看出当前归档日志文件的使用情况: se ...

  9. think ajax 应用

    首先得引入 jquery 文件,另外定义一个处理的 js.js 文件 如实现用 post 传输方法: 模板文件: <script type="text/javascript" ...

  10. CSS3鼠标移入移出图片生成随机动画

    今天分享使用html+css3+少量jquery实现鼠标移入移出图片生成随机动画,我们先看最终效果图(截图为静态效果,做出来可是动态的哟) 左右旋转 上下移动 缩放 由于时间关系我就不一步步解析各段代 ...