字符串的格式化

《C#高级编程》第6版190页。

  AppendFormat()实际上会在调用Console.WriteLine()时调用,它负责确定所有像{0:D}的格式化表达式应使用什么表达式替代。

  为了说明如何格式化字符串,看看执行下面的语句会得到什么结果:

  Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i);

  Console.WriteLine只是把参数的完整列表传送给静态方法String.Format(),如果要在字符串中以其他方式格式化这些值,例如显示在一个文本框中,也可以调用这个方法。带有3个参数的WriteLine()重载方法如下:

  public void WriteLine(string format, object arg0, object arg1)
  {
    Console.WriteLine(String.Format(format, arg0, arg1));
  }

  上面的代码依次调用了带有1个参数的重载方法WriteLine(),仅显示了传递过来的字符串的内容,没有对它进行进一步的格式化。

  String.Format()现在需要用对应对象的合适字符串表示来替换每个格式说明符,构造最终的字符串。但是,如前所述,对于这个建立字符串的过程,需要StringBuilder实例,而不是String实例。在这个示例中,StringBuilder实例是用字符串的第一部分(即文本“The double is”)创建和初始化的。然后调用StringBuilder.AppendFormat()方法,传递第一个格式说明符“{0,10:E}”和相应的对象double,把这个对象的字符串表示添加到构造好的字符串中,这个过程会继续重复调用StringBuilder.Append()和StringBuilder.AppendFormat()方法,直到得到了全部格式化好的字符串为止。

  下面的内容比较有趣。StringBuilder.AppendFormat()需要指出如何格式化对象,它首先检查对象,确定它是否执行System命名空间中的接口IFormattable。只要试着把这个对象转换为接口,看看转换是否成功即可,或者使用C#关键字is,也能实现此测试。如果测试失败,AppendFormat()只会调用对象的ToString()方法,所有的对象都从System.Object继承了这个方法或重写了该方法。在前面给出的编写各种类和结构的示例中,执行过程都是这样,因为我们编写的类都没有执行这个接口。这就是前面的章节中,Object.ToString()的重写方法允许在Console.WriteLine()语句中显示类和结构如Vector的原因。

  但是,所有预定义的基本数字类型都执行这个接口,对于这些类型,特别是这个示例中的double和int,就不会调用继承自System.Object的基本ToString()方法。为了理解这个过程,需要了解IFormattable接口。

  IFormattable只定义了一个方法,该方法也叫作ToString(),它带有两个参数,这与System.Object版本的ToString()不同,它不带参数。下面是IFormattable的定义:

  interface IFormattable
  {
    string ToString(string format, IFormatProvider formatProvider);
  }

  这个ToString()重载方法的第一个参数是一个字符串,它指定要求的格式。换言之,它是字符串的说明符部分,放在字符串的{}中,该参数最初传递给Console.WriteLine()或String.Format()。例如,在本例中,最初的语句如下:

  Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i);

  在计算第一个说明符{0,10:E}时,在double变量d上调用这个重载方法,传递给它的第一个参数是E。StringBuilder.AppendFormat()传递的总是显示在原始字符串的合适格式说明符内冒号后面的文本。

  本书不讨论ToString()的第2个参数,它是执行接口IFormatProvider的对象引用。这个接口提供了ToString()在格式化对象时需要考虑的更多信息——一般包括文化背景信息(.NET文化背景类似于Windows时区,如果格式化货币或日期,就需要这些信息)。如果直接从源代码中调用这个ToString()重载方法,就需要提供这样一个对象。但StringBuilder.AppendFormat()为这个参数传递一个空值。如果formatProvider为空,ToString()就要使用系统设置中指定的文化背景信息。

  现在回过头来看看本例。第一个要格式化的项是double,对此要求使用指数计数法,格式说明符为E。如前所述,StringBuilder.AppendFormat()方法会建立执行IFormattable接口的对象double,因此要调用带有两个参数的ToString()重载方法,其第一个参数是字符串“E”,第二个参数为空。现在double的这个方法在执行时,会考虑要求的格式和当前的文化背景,以合适的格式返回double的字符串表示。StringBuilder.AppendFormat()则按照需要在返回的字符串中添加前导空格,使之共有10个字符。

  下一个要格式化的对象是int,它不需要任何特殊的格式(格式说明符是{1})。由于没有格式要求,StringBuilder.AppendFormat()会给该格式字符串传递一个空引用,并适当地响应带有两个参数的int.ToString()重载方法。由于没有特殊的格式要求,所以也可以调用不带参数的ToString()方法。

  整个字符串格式化过程如图8-2所示。


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Racer graham = new Racer("Graham", "Hill", "UK", 14);
        Racer emerson = new Racer("Emerson", "Fittipaldi", "Brazil", 14);
        Racer mario = new Racer("Mario", "Andretti", "USA", 12);

        List<Racer> racers = new List<Racer>(20) {graham, emerson, mario};

        racers.Add(new Racer("Michael", "Schumacher", "Germany", 91));
        racers.Add(new Racer("Mika", "Hakkinen", "Finland", 20));

        racers.AddRange(new Racer[] {
            new Racer("Niki", "Lauda", "Austria", 25),
            new Racer("Alian", "Prost", "France", 51)});

        racers.Insert(3, new Racer("Phil", "Hill", "USA", 3));

        racers.ForEach(r => Console.WriteLine("{0:A}", r));
    }
}
class Racer : IComparable<Racer>, IFormattable
{
    public Racer() : this(String.Empty, String.Empty, String.Empty) {}
    public Racer(string firstname, string lastname, string country) : this(firstname, lastname, country, 0) {}
    public Racer(string firstname, string lastname, string country, int wins)
    {
        this.Firstname = firstname;
        this.Lastname = lastname;
        this.Country = country;
        this.Wins = wins;
    }

    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string Country { get; set; }
    public int Wins { get; set; }

    public override string ToString()
    {
        return String.Format("{0} {1}", Firstname, Lastname);
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (format != null)
            format = format.ToUpper();
        switch (format)
        {
            case null:
            case "N":  // Name
                return ToString();
            case "F":  // FirstName
                return Firstname;
            case "L":  // LastName
                return Lastname;
            case "W":  // Wine
                return String.Format("{0}, Wins: {1}", ToString(), Wins);
            case "C":  // Country
                return String.Format("{0}, Country: {1}", ToString(), Country);
            case "A":  // All
                return String.Format("{0}, {1} Wins: {2}", ToString(), Country, Wins);
            default:
                throw new FormatException(String.Format(formatProvider, "Format {0} is not supported", format));
        }
    }

    public string ToString(string format)
    {
        return ToString(format, null);
    }

    public int CompareTo(Racer other)
    {
        int compare = this.Lastname.CompareTo(other.Lastname);
        if (compare == 0)
            return this.Firstname.CompareTo(other.Firstname);
        return compare;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值