Language agnostic 您是否应该始终为其他情况编写代码;“永远不会发生”吗;?

Language agnostic 您是否应该始终为其他情况编写代码;“永远不会发生”吗;?,language-agnostic,conditional,Language Agnostic,Conditional,采取一些类似于 if (person.IsMale()) { doGuyStuff(); } else { doGirlStuff(); } 写这篇文章是为了显式地检查person.isFemale(),然后添加引发异常的新else吗?也许您正在检查枚举中的值,或者类似的东西。您认为没有人会向枚举添加新元素,但谁知道呢?“永远不会发生”听起来像是著名的临终遗言 我想你已经回答了你自己的问题。如果您知道您将永远不会看到其他值: bool isSet = ... if (isSet

采取一些类似于

if (person.IsMale()) {
    doGuyStuff();
} else {
    doGirlStuff();
}

写这篇文章是为了显式地检查person.isFemale(),然后添加引发异常的新else吗?也许您正在检查枚举中的值,或者类似的东西。您认为没有人会向枚举添加新元素,但谁知道呢?“永远不会发生”听起来像是著名的临终遗言

我想你已经回答了你自己的问题。如果您知道您将永远不会看到其他值:

bool isSet = ...
if (isSet)
{
    return foo;
}
return bar;

…那就不用麻烦了。但是,如果有可能存在两个以上的可能值,请遮盖住自己。你(或两年后的维护程序员)会为此感激的。

我发现“永远不会发生”听起来不错,直到几个月后,在一位同事添加代码、破坏了你的原始代码后,它咬了你的屁股。所以,对于我自己,我会确保我的if是实心的,即使它看起来不可能。

我想这取决于你的对象类型。如果它是严格的布尔型(或者通常是二进制的),那么,正如您已经猜到的,第二个测试是多余的。任何其他情况下,将进行第二次测试


然而,即使在这种情况下,你也可以有一个“心理捷径”来防止对象域的未来扩展——只要假设只有一个值是“真”值,而所有其他值都默认为“假”值。

如果你仔细阅读一些正式方法书籍,他们建议你做这种事

  • 定义post条件

    isMale && doGuyStuff || isFemale && doGirlStuff.
    
    if isMale: doGuyStuff
    
  • 派生一些将导致此post条件的候选语句

    if isMale: doGuyStuff
    
    这可以证明导致了一些后条件

    if isFemale: doGirlStuff
    
    这可以证明导致了一些后条件

    if isFemale: doGirlStuff
    
    请注意,顺序并不重要。事实上,如果去掉任何排序假设,就更简单了

  • 您将得到以下结果:

    if isMale: doGuyStuff
    elif isFemale: doGirlStuff
    
    请注意,
    else
    子句没有正确的用法。在形式派生中,您永远不会派生出
    else
    子句。你总会有一些条件是肯定的:
    a&&b | | c&&d
    诸如此类的事情。很少是
    a&&b | | |!a&&c
    ,但即使如此,您通常还是会得到显式的
    !一种
    状态

  • 从形式上讲,“不可能的其他”条款应限于做以下事情

        if isMale: doGuyStuff
        elif isFemale: doGirlStuff
        else:
            raise HorrifyingSituationError
    
    如果您提出了可怕的情景错误,这意味着您的计算错误,并且从条件中不正确地导出了语句。或者您首先不正确地定义了post条件


    无论哪种方式,该计划都是以一种深刻而绝对的方式设计错误的。一般来说,这并不奇怪。它通常在您第一次尝试测试它时失败得惊人。除非(通常情况下)您选择的测试数据反映了post条件原始定义中的错误。即使如此,一旦遇到此异常,您也可以轻松地跟踪并永久修复它。

    @Michael Petrotta-您的代码片段是不正确的,因为执行的DoY()操作忽略了条件的真值


    (很抱歉,现在还不能添加评论…

    我没有机会为我的公司编写代码,但有很多情况下,我们的程序员已经为这种情况编写了代码,但这种情况永远不会发生,而且在尝试解决客户报告的问题时,它非常有用。这似乎经常发生在我们从海外项目获得的代码中


    当客户打电话说“嘿,我遇到了一个“类型不允许的错误”,它说“类型不允许的鸭子”时,我们很快找到了问题的原因并解决了它。

    如果它永远不会发生,那么你就不需要为它编写代码。

    记住,C中的枚举可能会咬到你:

    enum SwitchPosition 
    {
        Off = 0,
        On = 1
    }
    
    void SetLightStatus(SwitchPosition pos)
    {
        switch (pos)
        {
            case On: 
                TurnLightOn(); 
                break;
            case Off: 
                TurnLightOff(); 
                break;
            default:
                // Fugedaboudit -- will never happen, right?
        }
    }
    

    错误!调用
    SetLightPosition(2);
    是合法的,并且会在switch语句中失败。最好按照S.Lott的建议抛出一个
    可怕的情景错误。

    就我个人而言,我会将
    else
    行写为:

    } else /*if (person.IsFemale())*/ {
    
    这避免了必须运行函数(如果不需要它的结果,我不愿意浪费时间运行它),但为未来的开发人员留下了重要的文档。现在很明显,条件的这个分支涵盖了
    IsFemale
    案例(具体而言),而不是
    !IsMale
    案例(一般而言)。您基本上是在“大声”做出假设,这使得将来的更改不太可能错误理解您正在做的事情并破坏您的代码


    在我们的嵌入式系统中,可能很难分析和调试故障,因此我们通常会包括“不可能的”“
    else
    语句,让它们吐出错误消息并抛出异常。这通常仅限于开发版本,一旦代码稳定,它们就会被删除。

    如果您想指出“不可能发生”的代码块,请使用


    如果它不能在生产中发生(但可能在开发过程中发生),我将使用
    断言

    如果它不应该在生产中发生,但可能发生,我要么返回错误,要么抛出异常

    顺便说一下,这是我在某个地方找到的一个巧妙的C++技巧。由于如果断言不真实,许多断言宏将在消息框中显示其表达式,因此对于永远不应执行的分支,可以使用以下形式:

    if (person.IsMale())
    {
        AdmitEntranceToManCave();
    }
    else
    {
        ASSERT(!"A female has gotten past our defenses. EVERYBODY PANIC!");
    }
    

    字符串文字的计算结果为(非空)地址,逻辑上为TRUE。因此,使用逻辑NOT运算符会使其为假。

    问题取决于您的上下文——是否还有其他人能够扩展您的Person对象?当机器人成为法人时,你可能会发现isMale()和isFemale()都是假的

    这一点尤其重要
    if(male)
    {
      ...
    }
    else
    {
      ...
    }
    
    // other stuff that nobody cares about...
    assert(male||female);