缺失格式字符串参数不应该是编译时错误吗?

7
在格式字符串的完整文本在编译时是静态和已知的情况下,缺失的格式字符串参数不应该是编译时错误,或者至少应该是警告吗?
ReSharper会捕获这个问题,但只是下划线波浪线。我认为这应该触发一般的编译时错误。
string x = string.Format("soeuotnh {0}");

有没有方法可以在出现这种错误时触发警告,而不必将我的代码通过FxCop或其他东西运行?即使是C/C++编译器也会为如此明显的错误触发警告/错误(尽管它们通常不会检查类型安全)。

3
编译器不会解析你的格式字符串,所以它怎么知道你缺少了一个参数? - M.Babcock
4
String.Format 不是 C# 编程语言的一部分,编译器为什么要关心它? - John Saunders
2
我非常确定C/C++编译器也无法解析你的printf格式... - M.Babcock
1
伙计们,显然除了“你是100%正确的”之外的任何答案都是错误的。只需发布这个快速获得15分。 - Ed S.
我承认这是我不喜欢使用 String.Format 的原因,而是使用 + 来组合字符串。 虽然这样做不太好看,但我宁愿不出现运行时错误。 - piertoni
显示剩余12条评论
7个回答

2

这可能是一个警告,但它应该吗?嗯,我想这取决于编译器团队,毕竟String.Format不是C#语言的一部分。

这不是C语言;你没有调用未定义的行为或其他任何东西,该方法可以简单处理缺少的参数,假设(我猜...)你在它抛出异常时会很快注意到。这并没有什么“危险”,只是逻辑错误。


实际上,它会抛出一个异常。 - Agent_L
我已经尝试了Mahmoud提供的代码,但出现了格式异常。 - Agent_L
@Agent_L:我也试过了...但是它需要一点时间才能弹出来,而在它弹出之前我就停止了程序 :D - Ed S.
编译器警告比两者都更可取。 - Mahmoud Al-Qudsi
1
@MahmoudAl-Qudsi:请阅读:http://blogs.msdn.com/b/ericgu/archive/2004/01/12/57985.aspx - Ed S.
显示剩余2条评论

2

我认为除了您提到的方法,没有其他自动检测此条件的方式。至于为什么会发生这种情况,Format方法是与(String, params Object[]) 重载匹配,其中参数在文档中定义如下:

public static string Format(
    string format,
    params Object[] args
)

格式 类型: System.String
组合格式字符串 (详见备注)。

参数 类型: System.Object[] 包含零个或多个要格式化的对象的对象数组。

params 关键字定义了可变数量的参数(包括零个参数),这就是为什么没有 编译时 异常抛出的原因。

编辑:

由于编译器可以选择有效的重载,所以您不会收到编译时错误。因此,这不再是编译器问题,您最好使用 Resharper 这个代码质量工具来检测此条件。


当然有“一种方法”,那就是将该功能添加到编译器中。在我看来,这并不值得,而且为一个不属于语言的方法调用构建一个特性也有点奇怪,因此它不存在。 - Ed S.
顺便提一下,它确实会抛出异常。 - Ed S.
我没有尝试过,但是它可能会抛出一个运行时异常(与编译时异常相对)。不过奇怪的是,文档明确指出“零个或多个对象”。 - mgnoonan
@MahmoudAl-Qudsi:因为 A)它不是语言的一部分,B)就该语言而言,它并不违法。为什么 C#(语言本身)需要了解String.Format - Ed S.
@MahmoudAl-Qudsi:实际上,编译器的唯一工作就是遵循规范。其他任何功能都只是锦上添花。不确定你从哪里得到这个想法。你是在说应该要求生活质量类型的功能,这太愚蠢了。此外,它只适用于一个用例。如果我的格式字符串是可变的,那么它无能为力。 - Ed S.
显示剩余4条评论

0
一些C编译器会解析格式字符串,因为格式字符串与参数不匹配可能导致严重的崩溃错误,甚至更糟糕的安全漏洞。然而,在C#中,你只会在调用处得到一个异常,所以这并不是关键问题。
编译器可以做到这一点,但这将需要代码将 string.Format 识别为特例,并且(更糟糕的是)还需要有人在 C# 编译器中编写格式字符串解析器。所有这些只是为了警告您每次执行时都会崩溃的代码。
问题在于许多人都打开了所有警告和将警告视为错误的选项进行编译,这意味着添加此警告将破坏他们的构建。任何在代码的某个角落里有一些错误格式字符串的人将突然无法构建他们的产品。这些人会抱怨。

2
抱歉,但那是我听过最可悲的借口。这正是问题所在:当它在“代码的某个黑暗角落”时,你不太可能在测试中捕获到它,这就是编译器应该抛出错误的原因。如果你在代码的某个角落有一个 int x = 7 / 0,那么这是一个编译时错误。而且你不应该忽略运行时异常,只因为它们被藏在某个地方! - Mahmoud Al-Qudsi
1
@MahmoudAl-Qudsi:为什么格式化字符串很特别?有许多运行时错误可以在编译器时捕获(例如将null传递给不接受null的方法)。按照您的推理,编译器也应该有特殊代码来查找所有这些错误--而这些错误不需要特殊的迷你语言解析器。 - Gabe

0

Resharper 显示此警告:“格式化字符串中不存在的参数”。


0
正如已经提到的那样,编译器不会也不应该关心这个问题。
当你进行以下操作时,你期望编译器如何行事?
string format = "{0}";
List<string> parms = new List<string> { "Hello" };

if (DateTime.Now.Second % 2 == 0)
{
    format += " {1}";
    parms.Add("World");
}

Console.WriteLine(format, parms.ToArray());

我并不期望它完全成功,但我希望它能够尝试。C/C++会尽最大努力分析您的字符串格式说明符,这对它们来说比C#编译器更加困难。 - Mahmoud Al-Qudsi
1
@MahmoudAl-Qudsi:除了在C语言中会引发未定义行为,而在C#中该行为是完全被定义的。在C中更加危险,所以这样做是值得的。 - Ed S.

-1

该方法的“format”参数不是常量。任何非常量都无法在编译时进行检查。

它可以通过各种方法在运行时组合,甚至可以是用户输入...(太可怕了!)

这就是为什么它不是编译时错误的原因。

编辑:好的,在它是一个常量表达式的情况下,它可以被分析并生成警告。但是这里有Eric Lipert在另一个问题上的答案,有一个成本效益的门槛。

是否值得为String.Format(可能还有其他字符串格式函数)花费特殊情况的时间和精力来实现这个真正的好处...这是一个艰难的抉择。


很多时候它是常量,这种情况下它可以被捕获。这不是一个技术问题,而是关注点和实用性的问题。考虑到String.Format甚至不是语言的一部分,这是否真的是一个足够大的问题来证明将其添加到编译器中?这是一个已经做出的决定。 - Ed S.
@JeroenLandheer 这并不是真的。以下是一个简单的示例,即使使用了const字符串,也会抛出异常而不会有警告。(我不知道你从哪里得到了“只有const可以被编译器读取”的说法):const string format = "blabla{0}"; string x = string.Format(format); - Mahmoud Al-Qudsi
1
@MahmoudAl-Qudsi:你不明白为什么只有常量表达式才能被编译器分析吗?如果你已经知道“正确”的答案并对任何持不同意见的人都怀有敌意,那还问什么问题呢? - Ed S.

-1

我已经在Microsoft Connect上提交了考虑此功能的请求,因为我认为

  • 声称“如果代码路径不是很热门,人们更喜欢在运行时编译并失败”的答案是荒谬的,在非解释性语言中没有任何地方,特别是在像C#这样明确定义、强类型的语言中,它对类型安全性的重视程度如此之高。
  • 声称编译器在编译时无法(永远)推断格式字符串和参数数组的答案也是不正确的。是的,有时编译器无法在编译时推断其中一个(或两个)信息,这是无法避免的,但在许多其他情况下,它可以完全掌握所需的全部信息。
  • 声称这不是编译器的工作,只是因为规范没有提到为这种行为抛出警告的答案忽略了规范是编译器应该实现的最低要求,而编译器是自由的(并且确实在很大程度上)超越规范,只要它不与规范冲突。
  • 声称C编译器也不会这样做的答案是错误的 - 我已经发布了相反的例子
  • 声称我发布的代码示例在运行时不会引发异常的答案是明显错误的(尽管大多数已经被编辑过了)。
最终,这是一个“可能可以,可能不会,也许应该”的情况。我试图确保在编译器(而非第三方软件)中没有设置可以使其警告更加严格的选项,或者是否有一些方法可以更改我的代码以引发异常(例如,将所有内容声明为const),但目前为止似乎不可能。无论Visual Studio团队对我在MS Connect上开放的问题做出何种回应,事实仍然存在:编译器肯定可以在编译时捕获大量字符串格式化异常,这将是一个好处...但是否值得实现,这取决于VS团队的决定。

2
gcc != "一般的C编译器"。说“C不支持这个”是完全正确的,因为它不在语言规范中。有人声称“没有C编译器实现支持这个”吗?那就是另外一回事了。 - Jon Skeet
我认为很明显我在指的是最新版本的 Microsoft C# 编译器(csc),针对最新版本的运行时。然而,事实上,这个抱怨同样适用于任何过去版本的 csc 和 Mono dmcs 编译器。我不知道是否还有其他编译器存在,但这都不是重点。说实话,我喜欢 C#,这只是建设性的批评,旨在使整个开发体验更加友好。我不知道为什么每个人都对此如此防御。 - Mahmoud Al-Qudsi
说实话,我认为你的语气并没有起到帮助作用。我认为你本可以用更具建设性的方式提出类似的问题(附上更加友好的评论),从而获得更多的支持。 - Jon Skeet
1
我确实失去了耐心,很抱歉。但是我对“让我们捍卫C#,这个家伙试图说C++更好”的蜂群思维感到沮丧,这种思维体现在(一些)事实不正确、匆忙回答的形式中。如果您参考原帖,它非常无害、好奇和真诚。我并不想发动战争,很抱歉它变成了这样。 - Mahmoud Al-Qudsi
你说得对。那可能很容易被误解,我没有好好考虑过。"明显的bug"并不是指C#,而是指开发人员编写的代码质量差,经过进一步审查后显然存在缺陷。我想你会同意,在原始帖子中发布的代码行包含一个"明显的bug"吧?我一定被误解了。 - Mahmoud Al-Qudsi
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接