c#中,对于字符串的处理,使用起来比较方便【String类型被定义为基元类型】,当程序中出现literal(字符串常量时)会直接应用到string上【个人一般习惯写小写keywords(语法着色),这里string和String类是对应的,类似object和Object类】
string是不可变的【immutable】,string类型中的大多数方法返回的都是一个新构造的字符串,使用者不用担心破坏原来的输入串,由于不可变,也有了线程安全性。
稍微注意的是:
string s = new string("hello"
这样的语句编译器是不允许的【不允许以串为参数来动态构造string(不过不安全【unsafe】的代码,比如传人char*,byte*
等等是允许的】,实际应用中也大多是string s = "hello"
这种方式来书写【这里只是提一下规则】
由于程序中对string的大量使用,因此,string类型和CLR是直接相关的。CLR会用一些特殊的方式来构造string。【不再newobj,而是ldstr】
string也允许像c语言那样,直接在串中提供一些特殊字符来方便实用【比如hello \r\n world
来进行换行】,对于换行推荐的方式是使用Environment.NewLine
;
对于路径,文件名,正则应用中,可以通过在串前输入@来避免串中特殊字符的转义。
例如:
string s = "c:\\windows\\system32";
//和
string s = @"c:\windows\system32";
//是等价的【产生相同的IL代码】,但是第二种无疑更方便书写和阅读。
如果程序中会用到大量相同的字符串,我们可以使用字符串拘留技术【String.Intern()】
string s1 = "hello";
string s2 = "hello";
System.Console.WriteLine(Object.ReferenceEquals(s1,s2));
//返回false【不过4.0之后会返回True,可能是4.0对此进行了某种优化】
string s3 = string.Intern("world");
string s4 = string.Intern("world");
System.Console.WriteLine(Object.ReferenceEquals(s3,s4));
//返回True,使用Intern可以确保这一点【不用过于担心CLR随着版本不同作出不同优化】
但是不要过分使用Intern字符串拘留,因为使用Intern,进行字符串拘留本身也要耗费时间。
string是不可变的,如果程序中大量用到需要动态构造获得的串,就可以考虑使用System.Text.StringBuilder类型。StringBuilder类型是可变的串。【内部维护一个char数组】。在构造完成后,直接调用类型的ToString()方法将其转为string即可。【具体用法可以查阅资料】
大致说完了常用的一些概念,这里说说字符串比较和输出和语言文化的概念。
语言文化在平时的个人编程中【特别是客户端】相对可能用的比较少。但是,不同的国家不用语言文化,也会导致对于字符串的输出和比较有些不同【这个只要提起就不难理解—-程序如果是面向其他国家其他地区的用户呢】
这里,先说输出,一般对于对象的输出,是调用类型中的ToString方法【ToString是object类型中的一个virtual修饰方法,如果在自己的实现类型中没有重写这个方法,那么就会调用object中(或者此类的其他实现了ToString()的基类)】,但是对于ToString()的使用,一般使用无参数的ToString()这有两个问题: 第一,无法控制字符串的格式【比如输出百分比,十六进制数字等】,第二,不能方便的选择一种语言文化【这在服务器端尤其麻烦明显】
因此,为了对字符串有更多的控制,应当实现IFormattable接口
public string ToString(string format, IFormatProvider formatProvider);
IFormattable接口中有一个带两个参数的ToString()方法,第一个参数format,作用是告诉方法该如何格式化对象第二个参数是System.IFormatProvider接口的一个具体实现类型,为ToString()提供了具体的语言文化信息。
对于第一个参数【format】如何格式化字符串,FCL【框架类库】定义的许多类型都可以识别很多种格式,例如DateTime中’d’表示短日期,“D”表示产日期等等【举了两个例子,具体查看MSDN】,所有内建的基数值类型都支持,“C”表示货币,”D”表示十进制格式等。【format往往取这些值。】
进行数字格式化时,应该用适合语言文化的信息来进行格式化。格式化数字时,ToString()方法会检查第二个参数【formatProvider】,如果是null,ToString()会读取System.Theading.Thead.CurrentThead.CurrentCulture属性来判断与调用线程关联的文化信息。CurrentCulture属性返回的是一个System.Globalization.CultureInfo类型实例,ToString()会读取CultureInfo的两个属性【NumberFormat和DateTimeFormat,究竟读取那个–根据当前要格式化的是一个数字还是日期来决定】,而这两个属性分别返回System.Globalization.NumberFormatInfo和System.Globalization.DataTimeFormatInfo【其中NumberFormatInfo中有一系列属性,不CurrencySymbol,CurrencyDecimalSeparator】。
调用IFormattable的ToString()时候,第二个参数【formatProvider】可以不传递null,而是传递一个对象引用【当然该对象实现了IFormatProvider接口】
public interface IFormatProvider
{
// 摘要:
// 返回一个对象,该对象为指定类型提供格式设置服务。
//
// 参数:
// formatType:
// 一个对象,该对象指定要返回的格式对象的类型。
//
// 返回结果:
// 如果 System.IFormatProvider 实现能够提供该类型的对象,则为 formatType 所指定对象的实例;否则为 null。
object GetFormat(Type formatType);
}
IFormatProvider接口的实现基本思路是: 如果一个类型实现了该接口【IFormatProvider】,就认为该类型的实例能提供符合语言文化的信息,从而忽略调用线程关联的语言文化信息【前面说了,无参ToString()会根据System.Theading.Thead.CurrentThread.CurrentCulture中的信心来决定语言文化】
一下引入如此多的类,是否有点乱,图一张小图,看起来就简单明了了。
Decimal d = 123.5m;
string s = d.ToString("C", new CultureInfo("en-US"));//$123.5
//改en-US为zh-CN输出 ¥123.5
Console.WriteLine(s);
不要疑惑,在FCL中,IFormatProvider有三个实现类型【没有猜错,上面的CultureInfo就是其中一个】,还有两个分别是NumberFormatInfo和DateTimeInfo。
通过上面程序输出应该可以看到语言文化对程序的影响了【美国货币是$,中国是¥等等】
字符串比较语言文化和编码等放在下一篇讲。