在异常之后使用else(或不使用)


9

考虑一下这段代码:

if (x == 1)
{
  throw "no good; aborting" ;
}

[... more code ...]

现在考虑以下代码:

if (x == 1)
{
  throw "no good; aborting" ;
}
else
{
  [... more code ...]
}

这两种情况的工作方式完全相同。第一种情况的优点是您不必将其余代码“括入”中else。第二个优点是遵循显式具有elsefor每个的实践if

任何人都可以提供任何有力的论据来支持一个胜于另一个吗?


5
您引用的明确做法else似乎是伪造的。通常,else除非向后弯曲,否则根本没有任何东西可放入该块中。

一致性?面对代码更改是否具有鲁棒性?可读性?
托马斯·爱丁

1
恩,我不太喜欢每个人都if需要一个的想法else。最后一个在我们的代码库上工作的程序员紧随其后(好吧,有时……这是精神分裂症)。结果,我们有很多完全无意义else { /* do nothing */ }的代码
乱七八糟的

4
“每一个假设都有别的”似乎是由云架构师以(愚蠢的)一致性的名义发布的一个奇怪的声明。我认为遵循该做法没有任何好处,甚至从未在任何地方听说过。
艾里克·迪特里希

这是多余的。如果使用的是.NET堆栈,则请安装ReSharper,它会提醒您删除所有多余的else语句。
CodeART 2012年

Answers:


16

您不应else无条件if中断控制流的分支之后添加,例如包含a 或a的分支。通过消除分支引入的不必要的嵌套级别,可以提高程序的可读性。throwreturnelse

throw当连续发生多个抛出时,单个看起来或多或少看起来可以接受的事情变得非常丑陋:

void myMethod(int arg1, int arg2, int arg3) {
    // This is demonstrably ugly - do not code like that!
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    } else {
        if (!isValid(arg2)) {
            throw new ArgumentException("arg2 is invalid");
        } else {
            if (!isValid(arg3)) {
                throw new ArgumentException("arg3 is invalid");
            } else {
                // The useful code starts here
            }
        }
    }
}

这个代码片段做同样的事情,但是看起来要好得多:

void myMethod(int arg1, int arg2, int arg3) {
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    }
    if (!isValid(arg2)) {
        throw new ArgumentException("arg2 is invalid");
    }
    if (!isValid(arg3)) {
        throw new ArgumentException("arg3 is invalid");
    }
    // The useful code starts here
}

+1正确。第二个OP案例迫使您仔细阅读,然后留下WTF。但是...总是尝试使方法简短。在200行方法中间返回也是不好的。
图兰斯·科尔多瓦

1
公平地说,如果您只是重复使用if,可以这样做else if
古凡特

2
@Guvante:每个if测试一个条件,如果条件为真,则处理它,除非条件为假,除非有其他事情发生,否则else if就没有必要了。我们办公室周围有一个术语,用于表示dasblinkenlight的第一个代码段:“ 弹球机”。
Blrfl 2012年

@Blrfl弹珠机哈哈,完美的类比+1
吉米·霍法

@Blrfl:我指的是重复的ifs是太多嵌套的一个不好的例子。无论如何,您都不应重复嵌套。我同意,除非您只在谈论少量的代码,否则没有理由包括else
古凡特2012年

5

我将您称为“显式其他”实践称为反模式,因为它掩盖了没有特殊情况代码作为if的其他事实。

当您只需要必要的代码流构造,并且将它们最小化时,通常会提高可读性/可维护性。这意味着多余的else和if,它们将为整个函数添加作用域,从而使跟踪和维护更加困难。

例如,您具有此功能:

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
    }
    else
    {
        oblogonToConfigure.Color = _validColors[0];
        oblogonToConfigure.ColorIndex = 0;
    }
}

现在的要求是,在配置过程中还应该指定oblogon的类型/类型索引,存在多个范围,有人可以放置该代码并以无效代码结尾,即

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validOblogons.Contains(oblogonToConfigure.Type))
    {
        oblogonToConfigure.Type = _validOblogons[0];
        oblogonToConfigure.TypeIndex = 0;
        if (_validColors.Contains(oblogonToConfigure.Color))
        {
            oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
        }
        else
        {
            oblogonToConfigure.Color = _validColors[0];
            oblogonToConfigure.ColorIndex = 0;
        }
    }
    else
    {
        oblogonToConfigure.TypeIndex = _validOblogons.IndexOf(oblogonToConfigure.Type);
    }
}

将其与原始代码是否以必要的最小控制流构造以及最小化的控制流构造进行比较。

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.Color = _validColors[0];
    }

    oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
}

现在,将意外地放入错误的范围或最终导致膨胀的范围变得更加困难,从而导致该功能的长期增长和维护重复。另外,很明显,通过此功能可能会带来什么,从而增强了可读性。

我知道,这个例子有些人为,但是我看过很多次了

SomeFunction()
{
    if (isvalid)
    {
        /* ENTIRE FUNCTION */
    }
    /* Nothing should go here but something does on accident, and an invalid scenario is created. */
}

因此,正式化那些关于控制流构造的规则,我认为这可能会帮助人们建立直觉,以使他们在开始编写类似代码时闻到某种气味。然后他们将开始写..

SomeFunction()
{
    if (!isvalid)
    {
        /* Nothing should go here, and it's so small no one will likely accidentally put something here */
        return;
    }

    /* ENTIRE FUNCTION */
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.