一、创建字符串
1、string是一个不可变的数据类型,一旦对字符串对象进行了初始化,该字符串对象就不能改变了。修改字符串内容的方法和运算符实际上是创建一个新的字符串。
例如:string text = "This is a introduction。";text += "This is a Text";从语法上看,后一行代码是把更多的文本添加到字符串中。实际上并非如此,而是创建一个新字符串实例,给它分配足够的内存,以保存合并起来的文本。最初的文本"This is a introduction。"复制到这个新字符串中,再加上额外的文本"This is a Text"。然后更新存储在变量text中的地址,使变量正确地指向新的字符串对象。旧的字符串对象被撤消了引用----不再有变量引用它,下一次垃圾收集器清理应用程序中所有未使用的对象时,就会删除它。所以需要对字符串变量进行多次操作时将使用下面介绍的内容。
2、StringBuilder
在使用String类构造一个字符串时,要给它分配足够的内存来保存字符串,但StringBuilder通常分配的内存会比需要的更多。开发人员可以选择显示指定,但如果没有显示指定,存储单元量在默认情况下就根据StringBuilder初始化时的字符串长度来确定。它有两个主要的属性:
2.1、Length 指定字符串的实际长度;
2.2、Capacity 是字符串占据存储单元的长度。
对字符串的修改就在赋予StringBuilder实例的存储单元中进行,这就大大提高了添加子字符串和替换单个字符的效率。删除或插入子字符串仍然效率低下,因为这需要移动随后的字符串。只有执行扩展字符串容量的操作,才会给字符串分配需要的新内存,才可能移动包含的整个字符串。在添加额外的容量时,从经验来看,StringBuilder如果检测到容量超出,且该容量中没有显示设置新值,就会使自己的容量翻倍。
例如:StringBuilder text = new StringBuilder("This is a introduction。", 100);text.Append("This is a Text");
一般,使用StringBuilder可以执行字符串的任何操作,String可以用于存储字符串或显示最终结果。
二、格式化字符串
格式字符串本身大都由要显示的文本组成,但只要有要格式化的变量,它在参数列表中的下标就必须放在括号中。在括号中还可以有与该项的格式相关的其他信息,例如可以包含:
1、该项的字符串表示要占用的字符数,这个信息的前面应有一个逗号,负值表示该项应左对齐,正值表示该项应右对齐。如果该项占用的字符数比给定的多,其内容也会完整地显示出来。
2、格式说明符也可以显示出来。它的前面应有一个冒号,表示应如何格式化该项。
例如:double d = 45;int i = 10;Console.WriteLine("The double is {0,10:E}.", d);
字符串的格式化
就上面的double d = 45;int i = 10;Console.WriteLine("The double is {0,10:E}.", d);示例,实际上过程是
[HostProtection (SecurityAction .LinkDemand, UI = true )]
public static void WriteLine(string format, object arg0)
{
Out.WriteLine(format, arg0);
}
跳转到下面的函数中
public virtual void WriteLine(string format, object arg0)
{
this .WriteLine(string .Format(this .FormatProvider, format, new object [] { arg0 }));
}
,其中上面这个方法中的 WriteLine 的定义为 public virtual void WriteLine (string value){//代码省略,只是显示string.Format返回的字符串},而string.Format的定义为
public static string Format(IFormatProvider provider, string format, params object [] args)
{
if ((format == null ) || (args == null ))
{
throw new ArgumentNullException ((format == null ) ? "format" : "args" );
}
StringBuilder builder = new StringBuilder (format.Length + (args.Length * 8));
builder.AppendFormat(provider, format, args);
return builder.ToString();
}
即最终跳转到StringBuilder的AppendFormat方法。【由于AppendFormat的内容比较多,这里不加显示,想了解具体方法体,可反编译System.dll查看】StringBuilder.AppendFormat()需要指出如何格式化对象,它首先检查对象,确认它是否执行System命名空间中的接口IFormattable。只要试着把这个对象转换为接口,看看转换是否成功即可。如果测试失败,AppendFormat()只会调用对象的ToString()方法,所有的对象都从System.Object继承了这个方法或重写了该方法。
但是,所有预定义的基本数字类型都执行这个接口,对于这些类型,特别是这个示例中的double,就不会调用继承自System.Object的基本ToString()方法。为了理解这个过程,需要了解IFormattable接口。
IFormattable只定义了一个方法,该方法也叫作ToString(),它带有两个参数,这与System.Object版本的ToString()不同,它不带参数。下面是IFormattable的定义:
[ComVisible (true )]
public interface IFormattable
{
// Methods
string ToString(string format, IFormatProvider formatProvider);
}
这个ToString()重载方法的第一个参数是一个字符串,它指定要求的格式。换言之,它是字符串的说明符部分,放在字符串的{}中。例如上面的示例double d = 45;int i = 10;Console.WriteLine("The double is {0,10:E}.", d);中,在double变量d上调用这个重载方式,传递给它的第一个参数是E,即格式说明符内冒号后面的文本。第二个参数暂时不介绍,但如果formatProvider为空,ToString()就要使用系统设置中指定的文化背景信息。下一个要格式化的对象是int,它不需要任何特殊的格式。由于没有格式要求,StringBuilder.AppendFormat()会给该格式字符串传递一个空引用,并适当地响应带有两个参数的ToString()重载方法。
示例
using System;
using System.Text;
namespace Text
{
class MainEntryPoint
{
static void Main()
{
Vector v1 = new Vector (1, 32, 5);
Vector v2 = new Vector (845.4, 54.3, -7.8);
Console .WriteLine("\nIn IJK format,\nv1 is {0,30:IJK}\nv2 is {1,30:IJK}" , v1, v2);
Console .WriteLine("\nIn default format,\nv1 is {0,30}\nv2 is {1,30}" , v1, v2);
Console .WriteLine("\nIn VE format\nv1 is {0,30:VE}\nv2 is {1,30:VE}" , v1, v2);
Console .WriteLine("\nNorms are:\nv1 is {0,20:N}\nv2 is {1,20:N}" , v1, v2);
Console .ReadLine();
}
}
struct Vector : IFormattable
{
public double x, y, z;
public Vector(double x, double y, double z)
{
this .x = x;
this .y = y;
this .z = z;
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null ) return ToString();
string formatUpper = format.ToUpper();
switch (formatUpper)
{
case "N" :
return "|| " + Norm().ToString() + " ||" ;
case "VE" :
return String .Format("( {0:E}, {1:E}, {2:E} )" , x, y, z);
case "IJK" :
StringBuilder sb = new StringBuilder (x.ToString(), 30);
sb.Append(" i + " );
sb.Append(y.ToString());
sb.Append(" j + " );
sb.Append(z.ToString());
sb.Append(" k" );
return sb.ToString();
default :
return ToString();
}
}
public Vector(Vector rhs)
{
x = rhs.x;
y = rhs.y;
z = rhs.z;
}
public override string ToString()
{
return "( " + x + " , " + y + " , " + z + " )" ;
}
public double this [uint i]
{
get
{
switch (i)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
default :
throw new IndexOutOfRangeException (
"Attempt to retrieve Vector element" + i);
}
}
set
{
switch (i)
{
case 0:
x = value ;
break ;
case 1:
y = value ;
break ;
case 2:
z = value ;
break ;
default :
throw new IndexOutOfRangeException (
"Attempt to set Vector element" + i);
}
}
}
private const double Epsilon = 0.0000001;
public static bool operator ==(Vector lhs, Vector rhs)
{
if (Math .Abs(lhs.x - rhs.x) < Epsilon && Math .Abs(lhs.y - rhs.y) < Epsilon &&
Math .Abs(lhs.z - rhs.z) < Epsilon)
return true ;
return false ;
}
public static bool operator !=(Vector lhs, Vector rhs)
{
return !(lhs == rhs);
}
public static Vector operator +(Vector lhs, Vector rhs)
{
Vector Result = new Vector (lhs);
Result.x += rhs.x;
Result.y += rhs.y;
Result.z += rhs.z;
return Result;
}
public static Vector operator *(double lhs, Vector rhs)
{
return new Vector (lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
}
public static Vector operator *(Vector lhs, double rhs)
{
return rhs * lhs;
}
public static double operator *(Vector lhs, Vector rhs)
{
return lhs.x * rhs.x + lhs.y + rhs.y + lhs.z * rhs.z;
}
public double Norm()
{
return x * x + y * y + z * z;
}
}
}