String 类库学习
1.字符.
在.NET framework 里面,字符总是表示成16位Unicode代码值.每个字符都表示程System.Char结构(一个值类型)的一个实例.System.Char类型非常简单.它提供了两个公共只读常量字段:MinValue(定义成'\0')和MaxValue('定义程'\ufff')等.Char类型里有许多静态方法跟实例方法.针对一个char的实例,可以调用静态GETunicodeCategory方法,这个方法指出这字符是控制字符,货币符号,小写字母,大写字母,标点符号等.当然还有很多静态方法:如ToUpperInvariant 一种忽略语言文化的方式,将一个字符转化为它的小写形式.作为一种替代方案ToUpper方法将字符转换为小写或大写形式,但是转化是要使用与调用现成关联的语言文化信息.实例方法则有Equals CompareTo GetNumericValue(返回字符的数值形式)
2.System.String 类型
在任何应用程序中.System.String都是用的最多的类型之一.一个String代表一个不可变的顺序字符集.String派生自Object,所以他也是个引用类型,String还实现IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string> 等接口...
- 构造字符串
许多编程语言都将String视为一个基本类型---也就是说,编译器允许在源代码中直接表示文本常量字符串.编译器将这些文本常量字符串放到模块的元数据中,并在运行时加载它们.
在C#中,不能使用New操作符从一个文本常量字符串构造一个String对象:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 String s =new String("hello") 6 } 7 }
- 字符串的不可变性
事实上CLR用一种特殊的方式构造文本常量String对象:把文本常量字符串放到元数据中,并在运行是加载引用它们.
String s = "HI\r\nthere",虽然编码了回车符跟换行符,但一般不建议这么做.可以用System.Environment定义一个只读的NewLine属性.它是根据你运行系统来判断的
字符串是不可变的.(immutable).也就是说,字符串一经创建便不能更改,字符串一旦被创建,便不能更改,不能变长,变短,或修改其中的任何字符.使字符串不能改变,有几个好处.
1.首先,它允许在一个字符串执行各种操作.而不实际更改字符串: if(s.ToUpperInvariant().Substring(2,1).EndsWith("exe")),在这期间会在堆创建两个新的字符串对象.
2.使用字符串不可变.还意味着在操纵或访问一个字符串时不会生线程同步问题.
3.CLR可通过一个String对象共享多个完全一致的String内容.这样能减少系统中的字符串数量,从而节省内存,这正式"字符串留用"(string interning)技术的目的.
- 字符串的比较
"比较"或许是字符串上执行的最常见的操作.一般会出于两方面的原因来比较字符串:判断相等性,或者对字符串进行排序.String类库里面提供了我们很多个可以判断字符串相等性或者对字符串进行比较排序时的方法.
我们首先看Equals.我们在比较两个字符串时是比较它们的地址还是比较两个字符串的值了.
首先看静态版本的
public static bool Equals(string a, string b);
再看:
public static bool Equals(string a, string b)
{
return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
}
我们再看EqualsHelper,它是循环遍历字符以看是否相同
private static unsafe bool EqualsHelper(string strA, string strB) { int length = strA.Length; if (length != strB.Length) { return false; } fixed (char* chRef = &strA.m_firstChar) { fixed (char* chRef2 = &strB.m_firstChar) { char* chPtr = chRef; char* chPtr2 = chRef2; while (length >= 10) { if ((((*(((int*) chPtr)) != *(((int*) chPtr2))) || (*(((int*) (chPtr + 2))) != *(((int*) (chPtr2 + 2))))) || ((*(((int*) (chPtr + 4))) != *(((int*) (chPtr2 + 4)))) || (*(((int*) (chPtr + 6))) != *(((int*) (chPtr2 + 6)))))) || (*(((int*) (chPtr + 8))) != *(((int*) (chPtr2 + 8))))) { break; } chPtr += 10; chPtr2 += 10; length -= 10; } while (length > 0) { if (*(((int*) chPtr)) != *(((int*) chPtr2))) { break; } chPtr += 2; chPtr2 += 2; length -= 2; } return (length <= 0); } } }
我们看看实例版的Equals
public bool Equals(string value) { if (this == null) { throw new NullReferenceException(); } if (value == null) { return false; } return (object.ReferenceEquals(this, value) || EqualsHelper(this, value)); }
这里的ReferenceEquals 就表示指向同一个实例,后面还有个EqualsHelperm就如上述一样遍历每个字符
我们在看看"==" 在String类搜索到"==(" 我们可以发现 是把"==" 操作符重载了:
public static bool operator ==(string a, string b)
{
return Equals(a, b);
}
所以其实比较字符串先是比较地址, 再是看二者字符串中是否有相同的字符数.相同再则遍历每个字符(除却文化差异).
未完待续....