【问题标题】:Why is "while" so popular in C#?为什么“while”在 C# 中如此受欢迎?
【发布时间】:2010-10-20 06:41:36
【问题描述】:

问题字段有点太短,无法提出我真正的问题。如果有人可以更好地概括它,请随意。

我真正的问题是:这些天我正在阅读很多其他人的 C# 代码,我注意到一种特定形式的迭代被广泛传播,(见代码)。
我的第一个问题是:

所有这些迭代都是等价的吗?

我的第二个是:为什么更喜欢第一个?它与可读性有关吗?现在我不相信第一种形式比 for-form 更易读,一旦你习惯了它,可读性在这些结构中太过主观了,当然,你使用最多的东西看起来更具可读性,但我可以向大家保证,for-form 至少具有同样的可读性,因为它全部在一行中,您甚至可以阅读构造中的初始化。

因此第二个问题:为什么第 3 种形式在代码中很少见?

        // the 'widespread' construct
        int nr = getNumber();
        while (NotZero(nr))
        { 
            Console.Write(1/nr);
            nr = getNumber();
        }

        // the somewhat shorter form
        int nr;
        while (NotZero(nr = getNumber()))           
            Console.Write(1 / nr);            

        // the for - form
        for (int nr = getNumber(); NotZero(nr); nr = getNumber())
            Console.Write(1 / nr);

【问题讨论】:

  • Svante,tx,我应该更好地观察语言,但我的母语不是英语,你肯定猜到了。
  • 问题字段“太短”的原因是什么?我还没有看到任何限制......
  • 恐怕我做了一个 - 显然是错误的 - 假设。
  • 为什么使用自定义 NotZero 函数而不是 !=0(节省四个字符、提高可读性等...)?
  • 为什么要自定义 NotZero --> 为了这个例子。我想要一个外部和平的 logix 测试来增加一点趣味:)

标签: c# for-loop iteration while-loop


【解决方案1】:

所有这些迭代都是等价的吗?

是的

为什么更喜欢第一个?有没有。与可读性有关?

因为您可能希望将 nr var 的范围扩展到 while 循环之外?

为什么第 3 种形式在代码中很少见?

它是等价的,相同的陈述! 您可能更喜欢后者,因为您不想扩展 nr 变量的范围

【讨论】:

  • 因为您可能希望将 nr var 的范围扩展到 while 循环之外? --> 好点,虽然在典型用途中,你不会
  • 这没有意义,因为在 while 循环之外,nr var 将只保存分配给它的最后一个值——或者如果不满足 while 条件,则没有值。跨度>
  • 有时是有道理的,但事实并非如此
  • 我不同意最后的编辑。在所有情况下,getNumber() 的执行次数都相同。
【解决方案2】:

我认为人们经常使用 while() 循环,因为它最能代表您在脑海中可视化任务的方式。我认为与任何其他循环结构相比,使用它有任何性能优势。

【讨论】:

  • 在任何循环结构中都没有性能优势。
【解决方案3】:

您显示的第一种和第三种形式重复了对 GetNumber 的调用。我更喜欢第二种形式,尽管它的缺点当然是在一个条件下使用了副作用。然而,我几乎用一个while循环来做到这一点。通常我不会将结果作为参数传递 - 我发现自己的常见情况是:

string line;
while ( (line = reader.ReadLine()) != null)
...

int bytesRead;
while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
...

这两个现在对我来说都非常惯用,它们不会给我带来任何问题 - 正如我所说,它们允许我只将每个逻辑陈述一次。

如果你不喜欢有太多范围的变量,你可以引入一个额外的块:

{
  int bytesRead;
  while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
  {
     // Body
  }
}

就我个人而言,我不倾向于这样做 - “太宽”的范围并不会让我感到困扰那么

我怀疑编写一个封装所有这些的方法不会太难。比如:

ForEach(() => reader.ReadLine(), // Way to obtain a value
        line => line != null,    // Condition
        line =>
{
    // body
};

请注意,对于线阅读,我有一个课程可以帮助您:

foreach (string line in new LineReader(file))
{
    // body
}

(它不仅适用于文件 - 它非常灵活。)

【讨论】:

  • 乔恩,这种形式是第二个(有 3 个示例,而不是 2 个),奇怪的逻辑是为了示例。 (我只是在写一些正则表达式循环:for (theMatch = r.Match(inputString); theMatch.Success; theMatch = theMatch.NextMatch()) 但在示例中不想这样做
  • 我使用的可能也是同一种形式,但我为你改写了这个问题:“为什么使用广泛的形式而不是简短的形式?”也许您自己已经给出了答案:副作用引入的原因?但无论如何你都会使用它,所以我想这不是那么糟糕的做法吗?
  • 'while' 结构的问题是变量超出了循环的范围,即使它是不需要/不需要的。我总是更喜欢将变量保持在所需的最有限范围内。
  • @dribeas:我通常也喜欢更有限的范围,但我更喜欢避免重复。如果你愿意,你总是可以在“声明+while循环”周围加上大括号,所以范围就是那个块。我将编辑我的答案来证明这一点。
【解决方案4】:

这是一个随机的猜测:

当我编写 C# 代码时,我编写的仅有的两个循环结构是 while() 和 foreach()。也就是说,没有人再使用“for”了,因为“foreach”通常有效并且通常更优越。 (这是一个过度概括,但它有一个真理的核心。)结果,我的大脑不得不紧张地阅读任何“for”循环,因为它不熟悉。

【讨论】:

  • 是的,我可以看到。然而,这里涉及到一个测试条件,所以 foreach() 是另一回事。
  • 我不同意,就我发布的其他答案而言(您可以使用 foreach 的测试条件 - 例如 TakeWhile)。
  • 如果迭代长字符串数组,使用 for() 而不是 foreach()
  • 当索引很重要时使用
  • for 在迭代两个支持索引的结构时会派上用场。也就是说,直到我们在 C# 中获得 Zip
【解决方案5】:

我认为第三种形式(for-loop)是这些替代方案中最好的,因为它将事物置于正确的范围内。另一方面,不得不重复调用 getNumber() 也有点尴尬。

一般来说,我认为显式循环被广泛过度使用。高级语言应该提供映射、过滤和归约。当这些高级构造适用且可用时,循环就像使用goto 而不是循环一样。

如果映射、过滤或归约不适用,我可能会为这种循环编写一个小宏(不过,C# 没有这些,是吗?)。

【讨论】:

    【解决方案6】:

    我提供另一种选择

        foreach (var x in InitInfinite(() => GetNumber()).TakeWhile(NotZero))
        {
            Console.WriteLine(1.0/x);
        }
    

    其中 InitInfinite 是一个简单的辅助函数。整个程序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    class Program
    {
        static IEnumerable<T> InitInfinite<T>(Func<T> f)
        {
            while (true)
            {
                yield return f();
            }
        }
        static int N = 5;
        static int GetNumber()
        {
            N--;
            return N;
        }
        static bool NotZero(int n) { return n != 0; }
        static void Main(string[] args)
        {
            foreach (var x in InitInfinite(() => GetNumber()).TakeWhile(NotZero))
            {
                Console.WriteLine(1.0/x);
            }
        }
    }
    

    【讨论】:

    • 我认为乔恩和你有什么。类似,但我现在没有时间深入研究它,tx all,我认为这很有趣。
    • 真的吗?对我来说它看起来很自然,但我已经使用 F# 一年多了,所以我想我的大脑已经永久扭曲了。
    • 确实,Brian,我什至在看到谁回答之前就知道这是你的答案。因为 F#,我很久没有写过 for 或 while 循环了。折叠和递归真的接管了我的代码。
    【解决方案7】:

    至于为什么(1)和(2)比(3)“首选”,我的感觉是大多数人认为后者是一种迭代范围的方式,使用条件来定义范围,而不是而不是在某些条件仍然成立时继续迭代一个块。关键字语义适合这种解释,我怀疑,部分原因是,人们发现表达式在这种情况下最易读。例如,我永远不会使用 (1) 或 (2) 来迭代一个范围,尽管我可以。

    在 (1) 和 (2) 之间,我很伤心。由于紧凑性,我以前最常使用 (2)(在 C 中),但现在(在 C# 中)我通常写 (1)。我想我已经开始重视可读性而不是紧凑性,并且 (1) 似乎更容易快速解析,因此即使我最终重复了少量逻辑,对我来说也更具可读性。

    老实说,我很少再编写 while 语句了,通常在以前使用 while 语句的情况下使用 foreach 或 LINQ。想一想,我也不确定我是否使用了很多 for 语句,除非在单元测试中我生成一些固定数量的测试对象。

    【讨论】:

      猜你喜欢
      • 2015-05-23
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 2016-01-20
      • 2011-07-05
      • 2019-06-11
      • 2021-04-04
      • 2017-10-10
      相关资源
      最近更新 更多