最佳实践:在 switch 语句的 case 块中如何返回方法?

6

从switch语句中返回bool类型的方法哪种更好?我理解这可能是主观的,但我认为获取最佳实践意见对我们的职业非常重要 :)

public bool foo(param)
{
    switch (param)
    {
        case 1:
            if (something)
            {
                return true;
            }

            return false;    
        default:
            return false;
     }
}

- OR -

public bool foo(param)
{
    bool flag = false;

    switch (param)
    {
        case 1:
            if (something)
            {
                flag = true;
            }
            break;
        default:
            break;
     }

    return flag;
}

2
两个代码块 flag = truereturn true 嵌套了4层。 - Austin Salonen
那么Salonen先生,您会如何编写呢?如果您的答案更好,我会接受它 :)。 - Polaris878
8个回答

15

这里的区别在于单一返回点和多个返回点。

通常,单一返回点的代码倾向于做很多繁琐的工作(临时变量、在循环中检查这些变量),随着逻辑越来越复杂,代码会变得很棘手。我见过一些代码为了使用while (flag) ... flag = false;模式而极力奋斗,而不是while (true) ... break;模式,这样的代码很难理解。

我更喜欢多个返回点,因为它们尽早返回(没有额外的工作),并且不需要任何局部变量来帮助跟踪当前返回值。此外,我认为它们并不比单一返回点的代码更难读懂。

在c2上有一个很好的讨论(SingleFunctionExitPoint)。

我发现尽可能保持方法的“功能性”是一个好习惯(我在这里说“功能性”,因为我通常使用C#,除了LINQ之外,它不被认为是函数式的)。通过“功能性”,我的意思是尽量避免改变状态。引入更多状态,例如局部变量或成员,会使事情变得更加难以理解,因为现在你必须考虑它们的值以及这些变量如何被设置(来自另一个方法,来自另一个类...)。

总的来说,状态是邪恶的,但有时候是必要的邪恶。

这个问题在SO上也有一些有趣的讨论,请参考这里


1
我同意使用多个返回点,并且甚至可以说,如果您必须使用标志来路由执行路径,则应将该代码重构为使用多个返回而不是标志的方法。 - Arne Claassen

6

我更喜欢第二种方法。
它更加灵活,例如您可以在函数末尾添加一些依赖于flag的工作

此外,如果您只有一个情况,可能需要考虑使用其他东西而不是开关。


到达return语句并不会立即导致函数返回。相反,编译器将自动调用本地变量的析构函数。这是在C++中进行函数结束清理的首选方式。 - Ben Voigt
1
如果您的工作依赖于标志(flag),那可能是您的函数做了太多事情的迹象。 - Dykam
@Ben Voigt,这是关于C#的事情,它没有像析构函数这样的东西,如果有(Dispose),那么只要该对象被包装在using块中,它就会自动调用。 - Dykam
哎呀,我以为这个问题被标记成了C++。在C#中,你可以使用 try/finally (其中 using 是一种特殊情况),在返回之前运行清理代码。 - Ben Voigt
这篇文章刚刚得到了一票,这让我多年来第一次看了它。我的观点在这方面已经发生了很大的变化。我现在更喜欢早期返回。 - Jean-Bernard Pellerin

3

我更喜欢提前返回-但总的来说,除非在最多三个选项之间进行选择,否则我会尽量避免使用switch。我尝试将switch隔离开来,使得具有switch的方法仅确定变量部分-其他所有内容都由调用代码处理。

在我看来,提前返回更容易阅读,因为当我遇到返回时,我不需要继续“阅读”。


当选项超过3个并且带有许多标志(和花哨的东西)时,它们往往会随着时间的推移而混乱,并且在我看来会使代码难以阅读。 - Goblin

2

这似乎是一个非常复杂的计划,旨在避免使用 && 运算符。布尔表达式很酷:

public bool foo(int param)
{
    return (param == 1) && something;
}

2
不想打破你的幻想(我也认为布尔表达式很酷:)),但我认为他选择了一个非常简单的例子... - Goblin
@Goblin:这里有冒泡的希望 :) 属于 if (mumble) return true; else return false; 类。 - Hans Passant

2

在这两个选项中,我都不建议使用,因为它们嵌套太深了--你应该调用一个返回布尔值的函数来代替。

针对单点返回和多点返回的问题(以及我之前的观点),我选择多点返回,因为你可以直接返回函数返回的值。

举个例子:

public bool foo(param)
{
    switch (param)
    {
        case 1:
            return MethodForParamEqualOne();
        default:
            throw new ArgumentException("Param value=" + param + " is not supported.");
    }
    //If you're doing stuff here, the method is likely doing too much.
}

最终,这些switch语句会被重构为对象的方法调用(这将改善switch语句带来的可怕嵌套)。如果您非常精通,甚至不需要在对象工厂中使用switch语句(可以使用xml配置、反射、数据库等方式实现)。

同意。最好避免使用switch语句。有更优雅的方法来实现相同的功能。 - Chuck Conway

1

我会完全删除 switch 语句:

public bool foo(param)
{
   if(param != 1)
   {
    throw new ArgumentException("Param value=" + param + " is not supported.");
   }

   return MethodForParamEqualOne();
}

1

在返回和条件块的选择方面,始终记得注释关键代码可能是个好主意。如果使用

while (true)

不要害怕在 break 代码上方添加

//警告 + 原因!

通常来说,大部分人会告诉你这有点过度杀伤,但一旦变得更加微妙起来,这可能非常重要。

for (int x = 0; /*external variable for condition*/ ; x ++)

我发现在快速尝试测试大量代码的小改变时,经常会帮助我防止代码崩溃,实际上你不必再阅读那么多代码,为其他人节省了一些时间。

说实话,这有点像花生,但我想分享一下对我有帮助的东西,希望也能帮到其他人。

bool a = false;

while(true)
{
   ReDraw();
   ReUpdate();       

   //WARNING! Critical breakpoint   <--- Edit , nobody likes a game you can't win :P
   a = CheckForWinGame();
   if (a) 
   { 
      break; 
   }
}

在这个例子中,我希望展示的是警告让我一眼就能看到,我可以干涉方法,但不能干涉最后一个。它不是完全可靠的,但很快。

1

这完全取决于我正在做什么。如果我完成了案例,那么我会返回结果--为什么要执行不必要的代码呢?如果我想要做更多的工作,那么我会设置一个标志或其他东西。使用正确的工具来完成正确的工作。


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