StringBuilder
为了获得更高的性能,StringBuilder的方法并不保证线程安全。如果我们的程序需要对StringBuilder对象做多线程操作,那么我们必须显式添加线程同步代码。
字符串特定格式与语言文化
如果我们希望自己的类型能为调用者提供格式和语言文化选择的话,我们就应该使其实现System.IFormattable接口:
public interface IFormattable
{
String ToString(String format, IFormatProvider formatProvider);
}
format参数告诉方法应该怎样来格式化对象;formatProvider参数是一个实现了System.IFormatProvider接口的类型实例,该类型为ToString方法提供了特定的语言文化信息。
默认情况下,系统使用和调用线程相关联的语言文化信息来格式化字符串。不带参数的ToString方法就是这样实现的。
当我们对数值、日期、时间进行格式化时,语言文化信息将会发挥作用。
当格式化一个数值时,ToString方法会首先查看我们传递给它的formatProvider参数。如果formatProvider参数值为null,那么ToString方法会通过读取属性System.Threading.Thread.CurrentThread.CurrentCulture的值来确定和调用线程相关联的语言文化。该属性返回一个System.Globalization.CultureInfo类型的实例。
利用CultureInfo类型实例,ToString方法会根据格式化的是一个数值、还是一个日期/时间来读取它的NumberFormat或DateTimeFormat属性。这两个属性分别返回一个System.Globalization.NumberFormatInfo或System.Globalization.DateTimeFormatInfo实例。
IFormatProvider接口原型:
public interface IFormatProvider
{
Object GetFormat(Type formatType);
}
IFormatProvider接口背后的基本思想:当一个类型实现该接口时,它的意思是该类型的实例知道如何提供特定语言文化的格式化信息,而和调用线程相关联的语言文化信息应该被忽略。
System.Globalization.CultureInfo类型是FCL中很少的几个实现了IFormatProvider接口的类型之一。另外两个是NumberFormatInfo(对数值进行格式化)和DateTimeFormatInfo(对日期/时间进行格式化)。
如果我们希望获取一个对象的字符串,而又不需要针对任何特殊语言文化进行格式化,那么我们应该调用System.Globalization.CultureInfo的静态属性InvariantCulture,并将其传递给ToString方法的formatProvider参数。
将多个对象格式化为一个字符串
String s = String.Format("On {0:D}, {1} is {2:E} years old.", DateTime.Now, "XiaoMing", 15);
当Format方法分析格式字符串时,它会调用替换参数0实现的IFormattable接口中的ToString方法,并为其传递"D"和null两个参数。类似地,Format也会调用替换参数2实现的IFormattable接口中的ToString方法,并为其传递"E"和null两个参数。如果替换参数的类型没有实现IFormattable接口,那么Format将调用它的无参ToString方法,最后向结果中添加的将为常规格式的字符串。String类型为静态方法Format提供了好几个重载版本,其中一个版本除了接受上面的参数外,还接受一个实现了IFormatProvider接口的对象,这样我们就可以使用自己指定的语言文化信息来格式化所有可替换参数了。
如果我们使用StringBuilder构造字符串的话,我们可以调用StringBuilder的AppendFormat方法。该方法也接受一个格式字符串,且也有一个版本另外接受一个IFormatProvider参数。
字符串编码与解码
一般情况下,我们应该尽量使用UTF-16或UTF-8编码。
当我们需要对一组字符进行编码或解码时,我们应该获取一个类型继承自System.Text.Encoding的实例。Encoding是一个抽象类,它提供几个静态属性,每个属性都返回一个类型继承自Encoding的实例。
Encoding类提供的静态属性有:UTF8、Unicode、BigEndianUnicode、UTF7、ASCII、Default。
除上途属性外,Encoding类还提供一个静态方法GetEncoding,该方法允许我们指定一个代码页(通过一个整数或字符串),并返回一个可使用该代码页来进行编码或解码的对象。
一旦我们有了一个类型继承自Encoding的对象,我们可以通过调用GetBytes方法将一个字符数组转换为一个字节数组;也可以通过调用GetChars方法、或更为有用的GetString方法将一个字节数组转换为一个字符数组。
所有继承自Encoding的类型都提供一个GetByteCount方法,该方法不用执行实际的编码操作便可以返回对一组字符进行编码所需的字节个数。同样,也有一个GetCharCount方法,它不用执行实际的解码操作便可返回对一组字节进行解码所需的字符个数。这两个方法的效率不太高,如果更在意速度,而不是一个精确的结果的话,我们可以调用GetMaxByteCount和GetMaxCharCount两个方法。
利用Encoding.GetString进行解码操作的问题在于,该方法不会保存方法调用之间的状态。这样如果接收从一个流中传来的字节流(如:网络流),可能分两次或多次接收才能接收完整的字符串。而我们利用GetString方法分两次或多次进行解码的话,可能由于接收字节流的不完整而造成字符的丢失。在这种情况下,要对成块的字节进行解码操作,我们应该获得一个类型继承自Encoding的对象,然后调用它的GetDecoder方法。该方法返回一个新构造的、类型继承自System.Text.Decoder的对象。该对象提供了两个方法:GetChars和GetCharCount。这些方法用于解码工作,其原理和Encoding的GetChars和GetCharCount方法类似。它们对字节数组中尽可能多的字节进行解码。如果字节数组中剩余的字节没能包含足够填充一个字符的字节数据,那么剩余的字节将被保存在解码器对象中。下一次调用这些方法时,解码器对象会使用剩余的字节,及新传递给它的字节数组。这就确保了成块数据可以被正确解码。
同理,如果我们希望对字符串进行成块的编码,我们应该调用Encoding对象的GetEncoder方法。该方法返回一个新构造的、类型继承自System.Text.Encoder对象。该对象提供了GetBytes和GetByteCount方法用于编码字符串。
Base-64字符串编码与解码
Base-64编码用于将一个字节序列编码到一个base-64字符串中。
FCL中,base-64的编码和解码是通过System.Convert类型提供的静态方法实现的。
Convert.FromBase64String或Convert.FromBase64CharArray用于将一个base-64字符串编码为一个字节数组。
Convert.ToBase64String或Convert.ToBase64CharArray用于将一个字节数组解码为一个base-64的字符串。