写在前面 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    在经过了一个昏天黑地的假日之后,终于决定要做点儿什么了。这次决定说说属于地球上的程序员的共同话题--字符串。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    为什么这么说呢?其实很简单,字符串是人机交互的重要元素。用户通过键盘,向计算机传入的绝大部分内容都是字符串,而计算机的计算结果,也有相当一部分要转换为字符串向用户显示。字符串是一个程序员必须要处理,而且要处理好的问题。记得上学时,谭浩强的《C程序设计》中就用很大的篇幅介绍了字符串数组。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    这第一篇,就以.NET Framework 中的字符串,String类入手。今后还要继续介绍我所接触过的String类,比如CString类、string(STL)类、BSTR 等等……ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
获得String 类代码 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    以前是用Reflector  现在可以通过Visual Studio 2008的源代码调试的方式获得String类的源代码。具体方法可以参考横刀天笑 的文章。要Load System.dll 的符号表文件,然后再将调试断点设定在一行字符串操作代码上,最后按F11键,就可以看到全部的String 类代码了。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
“字符串是不可变的”以及字符串内存管理ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    这句话有点儿难理解。其实,这句话是指字符串占用的空间。字符串的内存空间一旦申请了,就没法随意的伸缩了。如果需要修改字符串内容的大小,比如添加几个字符,就需要重新向内存申请空间。在字符串使用者看来,这没什么。但是对于计算机和OS的内存管理来说,就是一个比较大的挑战。下面列举一种情况:假设你有一篇小诗要放入内存,大概占用了318个字节。你还有一篇文章要保存,占用了内存5K的空间。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      此时,如果将318个字节释放掉,然后要放入一个800字节的字符串。那么对于内存管理来说可能就是一个噩梦。因为虽然6K的内存有1K的剩余空间,但是地址并不连续。其实,有足够的空间并不意味着就可以存放对象,还要看存放对象的空间内存地址是不是连续的。当然,对于上述情况,绝大多数情况下OS的内存管理是不会弹出一个“Out of memory”了事儿的。OS内存管理模块会依据算法将某些内存页调出,保存到磁盘,再修改内存映射表…… 总之,会让计算机忙上一阵子。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      如果我们在堆内存中频繁的申请、释放一个或者几个字节,那么要不了多久,在可用内存总数不变的情况下,内存空间就会“千疮百孔”也就是我们常说的内存碎片。好在.NET CLR 的GC可以对内存碎片进行整理,但这也是一个非常耗时的工作。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      那么对于这么头疼的问题有什么好办法么? 一种解决办法是,给内存设定阀值,比如,设定最小内存申请空间为:512个字节。当申请318个字符的存储空间时,OS实际上分配了512个字节。这样保证内存的申请和释放都是一些相对“完整”的内存“块”以减少内存碎片的产生。 还有一种办法就是尽量遵循“不可变”的原则,减少字符串在创建后的伸长和缩短的操作。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      说了这么多,只是让看客们了解一些基础知识,以便更好的理解string类的代码。言归正传……ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
String 类概述ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    String 类源代码一共有3257行。作为容器(容纳字符的容器),String 类继承了IComparable、ICloneable、IConvertible、IEnumerable 接口,以便调用者枚举、拷贝、转换容器中的字符。在声明中还有下面的代码:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
#if GENERICS_WORKŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
        , IComparable<String>, IEnumerable<char>, IEquatable<String> ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
#endif
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    这表示源代码的编写者考虑到了对泛型的支持。如果使用C# 2.0 标准的编译器编译代码,那么String 类还需要继承这三个泛型接口。String 类的方法约有80个(不包括重载形式)。我大概给他们分了分类:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
1) 内存申请函数,FastAllocateString。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
2) 构造函数,提供了8种构造的重载形式用来构建字符串对象。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
3) 字符串操作函数,例如Insert、Join、Substring等,可以对字符串的内容进行修改。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
4) 字符串特征函数,例如IsNullOrEmpty、IsNormalize等,向调用者返回当前字符串内容的特征。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
5) 格式化字符串函数,Format。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
6) 接口实现函数,如String.Iconvertible.ToBoolean() 等。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
7) 内部辅助函数。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
内存申请函数ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      FastAllocateString(int iLength) 这个函数不在String.cs中实现,其声明为:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
private extern static String FastAllocateString(int length); 具体实现不详(为此苦恼了很久……)。但从该函数的声明可以看出,该函数可以在托管堆上申请一片指定长度的内存,并返回一个String类型的引用地址。在String类中,凡是需要申请内存的地方,都是使用这个函数。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
构造函数ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      在8种构造函数中,最基本的构造函数的声明:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      unsafe public extern String(sbyte *value, int startIndex, int length, Encoding enc);
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      也就是说,要给定一个指向字符串首地址的指针(sbyte *value),要创建的字符串在给定的字符串中的起始索引位置(int startIndex),字符串长度(int length),以及编码对象(Encoding enc)。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      其实,不论那种构造,最终都是调用CreateString静态函数创建的字符串对象。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
1 unsafe static private String CreateString(sbyte *value, int startIndex, int length, Encoding enc) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
2            if (enc == null) ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
3                return new String(value, startIndex, length); // default to ANSI ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
4            if (length < 0)ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
5                throw new ArgumentOutOfRangeException("length",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
6            if (startIndex < 0) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
7                throw new ArgumentOutOfRangeException("startIndex",Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
8            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
9            if ((value + startIndex) < value) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
10                // overflow checkŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
11                throw new ArgumentOutOfRangeException("startIndex",Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR")); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
12            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
13            byte [] b = new byte[length];ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
14 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
15            try {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
16                Buffer.memcpy((byte*)value, startIndex, b, 0, length);ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
17            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
18            catch(NullReferenceException) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
19                // If we got a NullReferencException. It means the pointer orŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
20                // the index is out of range ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
21                throw new ArgumentOutOfRangeException("value", ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
22                        Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
23            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
24ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
25            return enc.GetString(b);ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
26        }
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    该函数在进行过参数检查后,首先在堆内存中,按照指定的长度创建了缓冲区b,然后使用System.Buffer的内存拷贝函数memcpy将指定的字符串内容拷贝到缓冲区b,最后调用编码对象Encoding的GetString方法,得到指定编码方式下的,经过正确编码的字符串。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
字符串操作函数ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    这部分包含的函数比较多,着重的说几个。Join函数,这个函数是用来进行字符串合并的函数。从这个函数里面,我们仍然可以看到字符串“不可变”的一面。代码如下:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
1 public unsafe static String Join(String separator, String[] value, int startIndex, int count) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
2            //Treat null as empty string. ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
3            if (separator == null) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
4                separator = String.Empty;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
5            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
6ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
7            //Range check the arrayŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
8            if (value == null) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
9                throw new ArgumentNullException("value"); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
10            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
11 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
12            if (startIndex < 0) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
13                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
14            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
15            if (count < 0) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
16                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
17            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
18 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
19            if (startIndex > value.Length - count) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
20                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
21            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
22ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
23            //If count is 0, that skews a whole bunch of the calculations below, so just special case that. ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
24            if (count == 0) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
25                return String.Empty;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
26            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
27 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
28            int jointLength = 0;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
29            //Figure out the total length of the strings in value ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
30            int endIndex = startIndex + count - 1; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
31            for (int stringToJoinIndex = startIndex; stringToJoinIndex <= endIndex; stringToJoinIndex++) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
32                if (value[stringToJoinIndex] != null) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
33                    jointLength += value[stringToJoinIndex].Length;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
34                }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
35            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
36 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
37            //Add enough room for the separator.ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
38            jointLength += (count - 1) * separator.Length; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
39 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
40            // Note that we may not catch all overflows with this check (since we could have wrapped around the 4gb range any number of timesŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
41            // and landed back in the positive range.) The input array might be modifed from other threads, ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
42            // so we have to do an overflow check before each append below anyway. Those overflows will get caught down there.ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
43            if ((jointLength < 0) || ((jointLength + 1) < 0) ) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
44                throw new OutOfMemoryException();ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
45            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
46ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
47            //If this is an empty string, just return. ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
48            if (jointLength == 0) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
49                return String.Empty;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
50            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
51ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
52            string jointString = FastAllocateString( jointLength );ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
53            fixed (char * pointerToJointString = &jointString.m_firstChar) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
54                UnSafeCharBuffer charBuffer = new UnSafeCharBuffer( pointerToJointString, jointLength); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
55ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
56                // Append the first string first and then append each following string prefixed by the separator. ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
57                charBuffer.AppendString( value[startIndex] ); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
58                for (int stringToJoinIndex = startIndex + 1; stringToJoinIndex <= endIndex; stringToJoinIndex++) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
59                    charBuffer.AppendString( separator ); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
60                    charBuffer.AppendString( value[stringToJoinIndex] );ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
61                }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
62                BCLDebug.Assert(*(pointerToJointString + charBuffer.Length) == \0', "String must be null-terminated!");ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
63            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
64ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
65            return jointString; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
66        }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    该函数,要求有4个参数。分隔符(String separator),要合并的小字符串组成的数组(String[] value),合并时数组的起始位置索引(int startIndex),要合并的小字符串数量(int count)。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    和其他函数一样,首先要进行必要的参数检查。然后,根据参数,计算出未来合并出来的大字符串的总长度:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            int jointLength = 0;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            //Figure out the total length of the strings in value ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            int endIndex = startIndex + count - 1; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            for (int stringToJoinIndex = startIndex; stringToJoinIndex <= endIndex; stringToJoinIndex++) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
                if (value[stringToJoinIndex] != null) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
                    jointLength += value[stringToJoinIndex].Length;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
                }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            //Add enough room for the separator.ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            jointLength += (count - 1) * separator.Length;
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
再然后,就是重新申请一个刚刚计算好长度的空间,用来存放新创建的字符串:ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
            string jointString = FastAllocateString( jointLength );ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
看到了吧?新字符串不是以第一个小字符串,也不是以最后一个小字符串为基础,叠加出来的。而是在内存中重新申请了空间。换句话说,Join函数没有“改变”任何一个小字符串。证明了字符串声明了之后是“不可变的”。紧接着53-61行代码,就是通过指针把每个小字符串中的数据放到新创建的大缓冲区中。最后,返回这个新创建的String对象。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
      再说说Concat方法。这个方法我们经常使用的是它的另一种形式,那就是我们经常会写这样的代码 string s = s1 + s2 + s3;编译器在编译这行代码时,就会将多个字符串相加变为Concat函数的调用:Concat(s1,s2,s3); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
1ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
2        public static String Concat(params Object[] args) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
3            if (args==null) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
4                throw new ArgumentNullException("args");ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
5            }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
6 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
7            String[] sArgs = new String[args.Length];ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
8            int totalLength=0; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
9 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
10            for (int i=0; i<args.Length; i++) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
11                object value = args; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
12                sArgs= ((value==null)?(String.Empty):(value.ToString()));ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
13                totalLength += sArgs.Length;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
14                // check for overflowŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
15                if (totalLength < 0) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
16                    throw new OutOfMemoryException();ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
17                } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
18            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
19            return ConcatArray(sArgs, totalLength);ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
20        }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    这个Concat函数使用了一个可以支持可变参数的关键字params,代码的7-18行仍然是要计算新字符串的长度。第19行,调用了ConcatArray函数进行实质上的连接。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
1private static String ConcatArray(String[] values, int totalLength) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
2            String result =  FastAllocateString(totalLength); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
3            int currPos=0;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
4 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
5            for (int i=0; i<values.Length; i++) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
6                BCLDebug.Assert((currPos <= totalLength - values.Length),ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
7                                "[String.ConcatArray](currPos <= totalLength - values.Length)"); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
8ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
9                FillStringChecked(result, currPos, values);ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
10                currPos+=values.Length;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
11            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
12ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
13            return result; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
14        }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    代码的第二行,就调用FastAllocateString方法申请了新字符串的空间,而后将字符串数组中的内容放入新的空间内。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
字符串特征函数ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    IsNormalized 方法是用来表明当前字符串是否是被指定的规格规范化了,用以表明当前字符串是以何种编码格式存储的。至于IsAscii和IsFastSort都是外部实现的,无法看到代码。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
格式化字符串函数ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
1public static String Format( IFormatProvider provider, String format, params Object[] args) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
2            if (format == null || args == null)ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
3                throw new ArgumentNullException((format==null)?"format":"args");ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
4            StringBuilder sb = new StringBuilder(format.Length + args.Length * 8);ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
5            sb.AppendFormat(provider,format,args); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
6            return sb.ToString();ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
7        }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    Format函数有多种重载,最基本的形式就是上面这样。参数有三:Format提供者(IFormatProvider provider), 格式字符串(String format)以及参数列表(params Object[] args)ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
    代码中,在进行了参数检查后,创建了StringBuilder对象,然后调用了StringBuilder的AppendFormat方法完成了实际参数的填充动作。ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  1 public StringBuilder AppendFormat(IFormatProvider provider, String format, params Object[] args) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  2            if (format == null || args == null) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  3                throw new ArgumentNullException((format==null)?"format":"args");ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  4            } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  5            char[] chars = format.ToCharArray(0, format.Length);ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  6            int pos = 0; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  7            int len = chars.Length; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  8            char ch = '\x0';ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
  9 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
10            ICustomFormatter cf = null;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
11            if (provider!=null) {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
12                  cf=(ICustomFormatter)provider.GetFormat(typeof(ICustomFormatter));ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
13                } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
14ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
15            while (true) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
16                int p = pos; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
17                int i = pos;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
18                while (pos < len) { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
19                    ch = chars[pos];ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
20ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
21                    pos++;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
22                    if (ch == '}') ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
23                    {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
24                        if(pos < len && chars[pos]=='}') // Treat as escape character for }} ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
25                            pos++; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
26                        elseŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
27                            FormatError(); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
28                    }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
29ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
30                    if (ch == '{')ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
31                    { ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
32                        if(pos < len && chars[pos]=='{') // Treat as escape character for {{ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
33                            pos++; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
34                        else ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
35                        {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
36                            pos--; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
37                            break;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
38                        }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
39                    }ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
40 ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
41                    chars[i++] = ch;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
42                } ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
43                if (i > p) Append(chars, p, i - p); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
44                if (pos == len) break;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
45                pos++; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
46                if (pos == len || (ch = chars[pos]) < '0' || ch > '9') FormatError();ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
47                int index =0;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
48                do {ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
49                    index = index * 10 + ch - '0'; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
50                    pos++;ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
51                    if (pos == len) FormatError(); ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
52                    ch = chars[pos]; ŒÖ’îO)\buRwww.netcsharp.cnÔ¯óã¿<š
53                } while (ch >= '0' && ch <= '9'