“for (bool flag = true; flag; flag = false) { ... }” - 这个正常吗?

4

我从未看到过这种初始化for循环的方法,也不明白为什么要这样写?

我正在研究如何在.NET中连接到IMAP服务器,并开始查看来自名为ImapX的库的代码。我发现一个方法中使用了for循环在NetworkStream中写入数据,并似乎在奇怪的for循环中读取响应。我不想逐字复制粘贴别人的代码,但是这是主要内容:

public bool SendData(string data)
{
  try
  {
    this.imapStreamWriter.Write(data);

    for (bool flag = true; flag; flag = false)
    {
      var s = this.imapStreamReader.ReadLine();
    }
  }
  catch (Exception)
  {
    return false;
  }

  return true;
}

再次强调,这并不是精确的代码,但它是一般思路。这个方法只是这样做,它不使用服务器响应,如果没有抛出异常,它只返回true。我不明白为什么要这样使用for循环; 如果有的话,有谁能解释初始化的优势是什么?


6
毫无疑问,这是没有意义的 - 这只会在被设置为false之前执行一次。如果旨在在ReadLine()上暂停,那么您可以免费获得它... - Yuck
1
我猜你对代码的要点理解有误了。像那样编写的循环只会执行一次。 - Justin Niessner
你确定你正确复制了for循环的那一行,并且flag没有在for循环体中被调整吗?按照现在的写法,它只会执行一次然后停止(从true开始 - 在true的情况下继续 - 在第一次重新循环时改为false - 现在停止!true)。 - Rudu
@Rudu,除非Reflector在隐藏代码,否则我省略的“循环”体的唯一部分是一个DEBUG测试,它将字符串写入控制台,没有其他内容。 - sellmeadog
@MarkSmith 没有错别字;我确实从代码中复制并粘贴了 for 循环的初始化。 - sellmeadog
显示剩余6条评论
4个回答

5
这是一种可怕的一次性执行循环的方式。如果您将flag初始化器更改为不总是true的内容,则可能会有略微更合理,但并没有多少意义。我曾经完全不认真地建议过这段代码:
Animal animal = ...;
for (Dog dog = animal as Dog; dog != null; dog = null)
{
    // Use dog...
}

...使用as运算符的一种方式,而不“污染”外部作用域。但这是语言的愚蠢之处,我从未真正使用过。


2
我之前认为你在查看原始源代码,但从你的评论中看来,似乎你在查看反编译工具Reflector基于MSIL还原的C#代码。重要的是要理解,这并不一定与原始编写的代码非常相似。
在MSIL级别上,不存在for循环或while循环,只有条件分支指令。(参见这里:http://weblogs.asp.net/kennykerr/archive/2004/09/23/introduction-to-msil-part-6-common-language-constructs.aspx) 任何试图重构C#代码的工具都必须对代码的原始结构进行一些猜测。这似乎不是最初作为for循环编写的,而是Reflector检测到该IL可能是由for循环生成的,其启发式算法做出了错误的猜测,认定它应该是for循环而不是其他语句。
如果我在ILSpy中查看相同的代码,它将呈现为while循环。它仍然多余,但看起来要正常得多。此外,原始代码实际上可能执行了一些已被优化的操作,例如调用[Conditional]方法或标记有#if指令的代码。另一方面,原始代码可能曾经执行过其他操作,但部分内容已被注释掉——注释不会保留在IL中。或者过去有更多的代码,但它们被直接删除了。
简而言之,在Reflector中看到的与最初编写的代码可能大不相同。你应该将其视为比IL更漂亮的C#呈现方式,而不是人类编写的C#代码的示例。

谢谢您的回答。我早就怀疑了,Reflector 显示给我的可能不是原始程序员编写的内容。我真的想知道,如果有的话,为什么会有人选择以那种方式编写循环,而所有其他答案的普遍共识(以及我的直觉)是没有理由这样做。再次感谢您详细的回答,让我很有道理。 - sellmeadog

1

我原本把这个当做评论,但我想这也可以作为答案。

这个“循环”是没有意义的。它将flag初始化为true,并声明只有在flag仍然为true时才执行。然而,在第一次迭代之后,flag被明确设置为false。因此,在这种情况下,它保证只运行一次。

我怀疑作者试图确保控制流会在ReadLine()上暂停 - 但无论如何,直到接收到用户输入,它都会这样做。


0

如其他人所指出的那样,for循环基本上是一个无操作。

但它确实做了一件事,可能重要也可能不重要:它引入了一个命名空间作用域。变量s的作用域是for循环体。一旦退出for循环,s就会超出作用域。您可以通过简单地创建一个块来获得相同的效果,例如:

{
  var s = this.imapStreamReader.ReadLine();
}

这还是相当愚蠢的。

不知道原作者想通过这种方式实现什么目的——也许是想确保他的s被销毁/垃圾回收/处理掉——但这种技术是行不通的(它不会起作用)。


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