Spiga

串(三):.NET Framework String类的实现(上)

2012-04-20 15:06:26

一、字符串类的概述

  String类继承了IComparable、ICloneable、IConvertible、IEnumerable接口,以便调用者枚举、拷贝、转换容器中的字符。同时还继承了IComparable, IEnumerable, IEquatable,这表示源代码的编写者考虑到了对泛型的支持。如果使用C# 2.0以上标准的编译器编译代码,那么String类还需要继承这三个泛型接口。
  String类是.NET Framework中最为重要的类型之一。
  不可变对象,提供了多种字符串操作函数。
  为StringBuilder类提供基础方法。
  为了保证执行效率,String类的性能关键部分采用非托管C++代码编写:
     - 数据寻址:例如:get_Chars等;
     - 内存分配:例如:FastAllocateString等;
     - 内存复制,移动。

二、System.String源代码的组成

  String.cs实现主要源代码,其他多个源代码文件实现辅助和基础操作功能。
  托管代码部分:
    – unsafecharbuffer.cs / buffer.cs提供辅助数据操作;
    – CultureInfo.cs/CompareInfo.cs为字符串比较,大小写处理提供和文化区域相关的代码实现;
    – Normalization提供规格化处理。
  非托管代码部分:
    – Ecall.cpp / ecall.h实现托管代码到非托管代码映射,FCFuncStart(gStringFuncs)部分;
    – Object.h实现String类的非托管结构映射和基本取值操作的功能;
    – Comstring.cpp实现复杂字符串操作;
    – Comnlsinfo.cpp/.h提供与文化区域相关的复杂字符串操作。

三、类的声明

  在String类一开始定义了3个非常重要的私有变量m_arrayLength、m_stringLength和m_firstChar,这3个私有变量在非托管代码中也会拥有同样的声明。以保证托管代码到非托管代码的映射。

public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String>
{
	//具体含义参见object.h中的StringObject类定义
	[NonSerialized]
	private int m_arrayLength;      //保存string对象所开缓冲区数组长度
	[NonSerialized]
	private int  m_stringLength;    //字符串实际长度
	[NonSerialized]
	private char m_firstChar;       //字符串的第一个字符,其地址为字符串实际的首字符
	//.................其它代码  
}

由于String类实现比较复杂,又设计到一些C++的代码。我也不能完全说清楚它的所有实现。后面的内容中我将只会谈到部分方法的实现,同时我不会涉及C++的代码实现(因为我也不是很明白)。

四、构造函数

  提供了8种构造的重载形式用来构建字符串对象

[CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
unsafe public extern String(char *value);
[CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
unsafe public extern String(char *value, int startIndex, int length);

[CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
unsafe public extern String(sbyte *value);
[CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
unsafe public extern String(sbyte *value, int startIndex, int length);
[CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
unsafe public extern String(sbyte *value, int startIndex, int length, Encoding enc);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern String(char c, int count);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern String(char [] value, int startIndex, int length);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern String(char [] value);

在8种构造函数中,最基本的构造函数的声明:
  unsafe public extern String(sbyte *value, int startIndex, int length, Encoding enc);

  从该构造函数中可以得知,要给定一个指向字符串首地址的指针(sbyte *value),要创建的字符串在给定的字符串中的起始索引位置(int startIndex),字符串长度(int length),以及编码对象(Encoding enc)。

  这些构造函数的都是通过外部非托管代码来实现的,具体的C++代码请参见object.h中的StringObject类定义。其实不论那种构造,最终都是调用CreateString静态函数创建的字符串对象。

unsafe static private String CreateString(sbyte *value, int startIndex, int length, Encoding enc) {
	if (enc == null)
		return new String(value, startIndex, length); // default to ANSI
	if (length < 0)
		throw new ArgumentOutOfRangeException("length",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
	if (startIndex < 0) {
		throw new ArgumentOutOfRangeException("startIndex",Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
	}
	if ((value + startIndex) < value) {
		throw new ArgumentOutOfRangeException("startIndex",Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
	}
	byte [] b = new byte[length];

	try {
		Buffer.memcpy((byte*)value, startIndex, b, 0, length);
	}
	catch(NullReferenceException) {
		throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));            
	}

	return enc.GetString(b);
}

五、内部辅助函数

1.内存申请函数:FastAllocateString

  该函数可以在托管堆上申请一片指定长度的内存,并返回一个String类型的引用地址。在String类中,凡是需要申请内存的地方,都是使用这个函数。该函数也通过非托管代码实现,其声明如下:

//内存申请函数
//该函数可以在托管堆上申请一片指定长度的内存,并返回一个String类型的引用地址。在String类中,凡是需要申请内存的地方,都是使用这个函数。
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern static String FastAllocateString(int length);

2.填充字符串函数:wstrcpy

  为了保证效率该函数进行了一些优化。首先在编辑器完成地址对齐操作,之后又每次以8字节(64位)为一次循环批次,进行数据复制。

//将smem中的charCount个字节复制到dmem中
//为了保证效率在进行实际的数据复制之前会在编译器完成对dmem与smem进行地址对齐操作
private static unsafe void wstrcpy(char *dmem, char *smem, int charCount)
{
	if (charCount > 0)
	{
		//如果地址没有以2字节对齐,将指针地址加1,使得地址以2字节对齐
		if (((int)dmem & 2) != 0)
		{
			dmem[0] = smem[0];
			dmem += 1;
			smem += 1;
			charCount -= 1;
		}

		//每次以8字节(64位)为此次,进行数据复制
		while (charCount >= 8)
		{
			((uint*)dmem)[0] = ((uint*)smem)[0];
			((uint*)dmem)[1] = ((uint*)smem)[1];
			((uint*)dmem)[2] = ((uint*)smem)[2];
			((uint*)dmem)[3] = ((uint*)smem)[3];
			dmem += 8;
			smem += 8;
			charCount -= 8;
		}
		if ((charCount & 4) != 0)
		{
			((uint*)dmem)[0] = ((uint*)smem)[0];
			((uint*)dmem)[1] = ((uint*)smem)[1];
			dmem += 4;
			smem += 4;
		}
		if ((charCount & 2) != 0)
		{
			((uint*)dmem)[0] = ((uint*)smem)[0];
			dmem += 2;
			smem += 2;
		}
		if ((charCount & 1) != 0)
		{
			dmem[0] = smem[0];
		}
	}
}

3.字符串索引器:this[]

   该操作实现代码为非托管函数COMString::GetCharAt。

//该操作实现代码为非托管函数COMString::GetCharAt
[System.Runtime.CompilerServices.IndexerName("Chars")]
public extern char this[int index] {
	[MethodImpl(MethodImplOptions.InternalCall)]
	get;
}

六、其它部分函数的实现

  1. 重载运算符
    public static bool operator == (String a, String b) {
           return String.Equals(a, b);
        }
    
        public static bool operator != (String a, String b) {
           return !String.Equals(a, b);
        }
    
  2. 字符串特征函数
     public static readonly String Empty = "";
    
        //也是通过非托管代码来实现的
        public extern int Length {
            [MethodImplAttribute(MethodImplOptions.InternalCall)]
            get;
        }
    
        public static bool IsNullOrEmpty(String value) {
            return (value == null || value.Length == 0);
        }
    
        public bool Contains( string value ) {
            return (IndexOf(value, StringComparison.Ordinal) >=0);  
        }
    
  3. 字符串操作函数
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    public extern String Insert(int startIndex, String value);
    
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    public extern String Replace (char oldChar, char newChar);
    
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    public extern String Replace (String oldValue, String newValue);
    
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    public extern String Remove(int startIndex, int count);
    
    public string Remove( int startIndex ) {
    	if (startIndex < 0) {
    		throw new ArgumentOutOfRangeException("startIndex", 
    				Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
    	}
    
    	if( startIndex >= Length) {
    		throw new ArgumentOutOfRangeException("startIndex", 
    				Environment.GetResourceString("ArgumentOutOfRange_StartIndexLessThanLength"));    
    	}
    
    	return Substring(0, startIndex);
    }   
    
      public Object Clone() {
    	return this;
    }
    
  4. 其它函数
    public String ToLower() {
    	return this.ToLower(CultureInfo.CurrentCulture);
    }
    
    public String ToLower(CultureInfo culture) {
    	if (culture==null) {
    		throw new ArgumentNullException("culture");
    	}
    	return culture.TextInfo.ToLower(this);
    }
    
    public String ToLowerInvariant() {
    	return this.ToLower(CultureInfo.InvariantCulture);
    }
    
    public String ToUpper() {
    	return this.ToUpper(CultureInfo.CurrentCulture);
    }
    
    public String ToUpper(CultureInfo culture) {
    	if (culture==null) {
    		throw new ArgumentNullException("culture");
    	}
    	return culture.TextInfo.ToUpper(this);
    }
    
    public String ToUpperInvariant() {
    	return this.ToUpper(CultureInfo.InvariantCulture);
    }
    
    public override String ToString() {
    	return this;
    }
    
    public String ToString(IFormatProvider provider) {
    	return this;
    }