如何避免 C# while 和 do-while 循环中的代码重复?

How can I avoid code duplication in C# while and do-while loops?

提问人:Jacob Archambault 提问时间:1/26/2020 更新时间:2/1/2020 访问量:543

问:

我在 C# 方法中有一个循环,该方法具有以下结构。

do
{
     getUserInput();
     if (inputIsBad)
     {
          doSomethingElse();
     } 
} while (inputIsBad);

或者,使用 while 循环:

getUserInput();
while (inputIsBad)
{
     doSomethingElse();
     getUserInput();
}

但这两种方法都使用冗余代码:do-while 同时具有 if 语句和 while 循环检查相同的条件;while 循环在循环之前和循环内部都调用 getUserInput()。

有没有一种简单、非冗余、非临时的方法来执行这些方法模式所做的事情,无论是一般的还是专门在 C# 中,只涉及编写每个基本组件一次?

C# 循环 与语言无关的 do-while

评论

0赞 user2864740 1/26/2020
可能:。当开始一条新线时,它看起来更好(对我来说)。请记住,操作员使用短路评估。作为常见情况,添加到用法中的值可以消除(包括赋值)。X v; while ((v = getUserInput()) != null /* assumes sentinel for boolean */ && isInputBad(v)) { doSomethingElse(v); }&&&&v
0赞 user2864740 1/26/2020
或者类似的东西,尽管我自己觉得这样“太棘手”了。while(isInputBad(v = getUserInput()) { doSomethingElse(v); }
0赞 Jonesopolis 1/26/2020
这是个好问题。这些年来,我遇到过几次这种情况,我不记得有哪个优雅的解决方案符合您的标准,并且一目了然。我会选择你的第二个例子

答:

2赞 user2864740 1/26/2020 #1

假设可以将其转换为生成布尔值的表达式*。getUserInput(..)

while (getUserInput()
    && isBadInput()) {
  doSomethingElse();
}

// Prompts for user input, returns false on a user-abort (^C)
private bool getUserInput() { .. }

注释中显示的其他变体(假定没有非本地状态)。

*微不足道的是,它始终可以编写为包装函数 - 请参阅 C#7 中引入的局部函数。(还有其他方法可以达到同样的效果,其中一些我认为“太聪明了”。

// local function
bool getUserInputAlwaysTrue() {
   getUserInput(); // assume void return
   return true;
}

while (getUserInputAlwaysTrue()
    && isBadInput()) {
  doSomethingElse();
}

在某些情况下,可以遵循这进一步推出逻辑。一般前提是:总是在下一个之前调用。getUserInput()isBadInput()

// local function or member method
// Prompt for user input, returning true on bad input.
bool getCheckedUserInput() {
   getUserInput(); // assume void return
   return isBadInput();
}

while (getCheckedUserInput()) {
  doSomethingElse();
}
0赞 rossum 1/27/2020 #2

我将使用一个布尔变量,您需要在循环主体外部声明该变量。这样,您只需要运行一次检查。我也把它变成了一种方法,因为这似乎更合乎逻辑。inputIsBad

bool badInput = true;  // Assume bad until checked -- failsafe.
do
{
  getUserInput();
  badInput = inputIsBad();
  if (badInput)
  {
    doSomethingElse();
  } 
} while (badInput);

评论

0赞 Jacob Archambault 1/31/2020
这包含与原始问题中首次声明的版本相同的冗余,同时添加了不必要的代码:布尔值仍在由 if 语句和 while 循环的条件检查,而对 badInput 的初始赋值从未使用(因为变量在检查之前在 do-while 循环中被重新赋值)。
0赞 rossum 1/31/2020
此版本仅运行一次。这大概比简单的布尔检查需要更长的时间。您的初始版本运行了两次。inputIsBad()
2赞 Kahou 1/27/2020 #3
do
{
     getUserInput();

     if (!inputIsBad) break;

     doSomethingElse();

} while (true);

评论

1赞 Jacob Archambault 1/31/2020
尽管我发现哨兵是临时的,但这种通用的解决方案很简单,易于阅读,并且消除了原始帖子中的代码重复。
1赞 Jacob Archambault 1/31/2020
一个小问题:将 while 设置为 true 实际上使“do”变得不必要,因此可以通过将其转换为 while 循环来进一步简化。
0赞 Jacob Archambault 2/1/2020 #4

基于 user2864740 的回答:

假设 getUserInput() 可以转换为一个函数,如果输入是好的,则返回 true,否则返回 true。假设其原始返回类型不是布尔值或 void,则根据大小写通过 out 或 ref 参数返回其原始返回值,例如

int originalReturnValue;

while (!getUserInput(out originalReturnValue))
{
     doSomethingElse();
} 

...

bool getUserInput<T>(out T output)
{
// method body
}