格式控件字符串的意外情况






3.86/5 (3投票s)
当您的列表项多于控件字符串中的项时会发生什么?
引言
我早就知道,参数数组中必须有足够的项,以满足格式项中分配的最高索引。今天的发现是,列表中未使用的项是完全无害的。
显然,相反的情况是不成立的;索引大于参数数组上限的项会引发 FormatException
异常。
背景
格式控制字符串几乎是每个 .NET 程序的一部分,甚至是最小的“Hello, World”程序,因为它们是 Console.Write()
和 Console.WriteLine()
中除了最简单的情况之外的隐式组件。只有极少使用的不带参数的重载才能在没有它的情况下工作;对于所有其他重载,它都是必需的参数。这同样适用于它的所有相关函数,如 TextStrimg.WriteLine()
、string.Format()
、StringBuilder.AppendFormat()
等。
这引出了参数数组的主题。如果你的列表有三个或更少的项,你可以不必正式定义一个数组,只需将它们列为第二个、第三个和第四个参数即可。然而,在内部,它们构成一个数组,即使只是一个隐式的数组。
Using the Code
一个简短的 C# 控制台程序很好地演示了以上所有要点。以下是整个程序,共 89 行,其中 50 行是注释,另外 14 行完全是空白,只剩下 25 行实际代码。在这 25 行代码中,有 4 行定义常量,1 行导入 System
命名空间,9 行打开或关闭作用域,剩下 12 行用于执行一些有用的操作。
using System;
namespace FormatItemCountDemo
{
class Program
{
static void Main ( string [ ] pastrCmdLneArgs )
{
// ----------------------------------------------------------------
// Format control strings defined as constants is my usual practice
// for internal programs. If I expect to distribute the program,
// these go into managed string resources. Having them defined as
// constants works well for this tutorial example.
// ----------------------------------------------------------------
const string ANNOUNCE_1 = @" Total Format Items in Array = {0}";
const string ANNOUNCE_2 = @"{1} Total Format Items in Array = {0}";
const string ANNOUNCE_3 = @"{1} Total Format Items in Array = {0}{2}";
const string DUMMY_FORMAT_ITEM_2 = @" (Dummy Format Item 2, with leading space)";
// ----------------------------------------------------------------
// I think it's worth the computational cost to initialize,
// even when the initial value is its default.
// ----------------------------------------------------------------
int intNStrings = 0;
Console.WriteLine ( "Begin format item counting demonstration.\r\n" );
// ----------------------------------------------------------------
// The first two writes succeed, because there are at least enough
// items in the parameter array to cover the highest index number
// that appears in the format control string.
// ----------------------------------------------------------------
Console.WriteLine (
ANNOUNCE_1 , // Format control string - one format item only,
// extra elemen6t in parameter array ignored
++intNStrings , // Format Item 0 = Counter, incremented before WriteLine sees it
Environment.NewLine ); // Format Item 1 = Embedded newline,
// unused because only one format item in format control string
Console.WriteLine (
ANNOUNCE_2 , // Format control string
++intNStrings , // Format Item 0 = Counter, incremented before WriteLine sees it
Environment.NewLine ); // Format Item 1 = Embedded newline,
// manifests as blank line above message
// ----------------------------------------------------------------
// The third write fails because it uses the same parameter array,
// but the format control string has a format item with an index of
// 2, which exceeds the highest index available from the array.
// ----------------------------------------------------------------
try
{
Console.WriteLine (
ANNOUNCE_3 , // Format control string - Format items account
// for all items in array, so all are consumed
++intNStrings , // Format Item 0 = Counter,
// incremented before WriteLine sees it
Environment.NewLine ); // Format Item 1 = Embedded newline,
// manifests as blank line above message
}
catch ( FormatException errBadFormatControlString )
{
Console.WriteLine (
"{2}{0} exception{2}{2} Exception Message: {1}" , // Format control string -
// causes FormatException because
// highest format item index is 2,
// but highest subscript
// in parameter array is 1
errBadFormatControlString.GetType ( ).FullName , // Format Item 0 = Full Name
// of Exception type
errBadFormatControlString.Message , // Format Item 1 = Message
// from Exception
Environment.NewLine ); // Format Item 2 = Embedded
// Newline
}
// ----------------------------------------------------------------
// Repeating with the same format control string and an extra item
// in the parameter array allows the formatted write to succeed.
// ----------------------------------------------------------------
Console.WriteLine (
ANNOUNCE_3 , // Format control string - Format items
// account for all items in array, so all are consumed
intNStrings , // Format Item 0 = Counter, incremented before WriteLine sees it
Environment.NewLine , // Format Item 1 = Embedded newline,
// manifests as blank line above message
DUMMY_FORMAT_ITEM_2 ); // Format Item 2 = Dummy format item, to make the statement work
// ----------------------------------------------------------------
// Careful attention to numbering items in the format array reduces
// the risk of format exceptions, though the method is not entirely
// foolproof.
// ----------------------------------------------------------------
Console.WriteLine (
"{0}Done!{0}" , // Format control string
Environment.NewLine ); // Format item 0 = Embedded Newline
// manifests as blank line above and below message
} // static void Main
} // class Program
} // namespace FormatItemCountDemo
在 Visual Studio 中创建一个新的控制台程序项目,将上面显示的所有内容粘贴到其中,然后构建。如果你想在交互式调试器中运行它时看到任何内容,请在 main 例程的右大括号处设置断点。或者,在构建它之后,你可以在输出目录中打开一个命令提示符,然后执行该程序,就像我创建下面显示的图片时所做的那样。
关注点
大多数有趣的位都包含在嵌入的注释中。如果你没有注意到,这些消息指示数组中格式项的数量;它们真正传达的是数组必须包含的满足格式控制字符串的最小格式项数量。
除了激发这篇文章的发现之外,代码中一个值得注意的特性是,每当我使用 Console
、WriteLine
、string.Format
以及其他格式化 string
的函数时,我都会注释参数数组。
由于前两行是从同一个数组打印出来的,因此很明显未使用的第二个项被完全忽略了。除了浪费一些字节之外,数组中的额外项是完全无害的。
第三个打印语句包含在 try
/catch
块中,以防止未处理的异常导致程序崩溃。在第三个打印语句尝试执行时生成的异常报告之后,第四个语句使用相同的格式控制字符串,但它向项目数组添加了另一个项目。有了三个项目,该数组涵盖了格式控制字符串中的格式项,并且打印成功。
这就是全部内容,除了将引用列表简化为基本内容 (System
) 之外,引用数量从 8 个减少到 3 个。演示包是一个完整的 Visual Studio 解决方案,包括调试和发布版本。构建完成后,该程序集以 Microsoft .NET Framework 的 4.5 版本为目标,尽管它当然可以以该框架的任何版本为目标,因为它很难说是推动了 Console.WriteLine
的极限,而 Console.WriteLine
自第一天起就是该框架的一部分。
历史
- 2016 年 6 月 12 日星期日,我发现了这个特性,创建了演示程序,并撰写了这篇文章。
- 2016 年 6 月 13 日星期一,我发现并纠正了两个拼写错误。