一、创建字符串
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 }));
}
(stringvalue){//代码省略,只是显示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;
-
}
-
}
-
}