如何避免“如果”链?

ksed 发布于 2019-05-22 c 最后更新 2019-05-22 22:23 201 浏览

假设我有这个伪代码:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}
executeThisFunctionInAnyCase();
executeStepX函数只有在前一次成功时才应执行。 无论如何,应该在最后调用executeThisFunctionInAnyCase函数。 我是一个编程新手,所以对于一个非常基本的问题感到抱歉:有没有一种方法(例如C / C++)来避免那个长的if链产生这种“代码金字塔”,代价是代码可读性? 我知道如果我们可以跳过executeThisFunctionInAnyCase函数调用,代码可以简化为:
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
但是约束是executeThisFunctionInAnyCase函数调用。 break语句能否以某种方式使用?
已邀请:

oest

赞同来自:

因为你在执行之间也有[...代码块...],我猜你有内存分配或对象初始化。通过这种方式,您必须关心清除已经在退出时初始化的所有内容,如果遇到问题则清理它,并且任何函数都将返回false。 在这种情况下,我的经验(当我使用CryptoAPI时)最好的是创建小类,在构造函数中初始化数据,在析构函数中你取消初始化它。每个下一个函数类都必须是前一个函数类的子代。如果出现问题 - 抛出异常。

class CondA
{
public:
    CondA() { 
        if (!executeStepA()) 
            throw int(1);
        [Initialize data]
    }
    ~CondA() {        
        [Clean data]
    }
    A* _a;
};
class CondB : public CondA
{
public:
    CondB() { 
        if (!executeStepB()) 
            throw int(2);
        [Initialize data]
    }
    ~CondB() {        
        [Clean data]
    }
    B* _b;
};
class CondC : public CondB
{
public:
    CondC() { 
        if (!executeStepC()) 
            throw int(3);
        [Initialize data]
    }
    ~CondC() {        
        [Clean data]
    }
    C* _c;
};
然后在你的代码中你只需要调用:
shared_ptr<CondC> C(nullptr);
try{
    C = make_shared<CondC>();
}
catch(int& e)
{
    //do something
}
if (C != nullptr)
{
   C->a;//work with
   C->b;//work with
   C->c;//work with
}
executeThisFunctionInAnyCase();
我想这是最好的解决方案,如果每次调用ConditionX初始化一些东西,分配内存等。最好确保一切都将被清理。

wab

赞同来自:

如果您的代码与示例一样简单,并且您的语言支持短路评估,那么您可以尝试这样做:

StepA() && StepB() && StepC() && StepD();
DoAlways();
如果您将参数传递给函数并返回其他结果,以便您的代码无法以先前的方式编写,那么许多其他答案将更适合该问题。

qanimi

赞同来自:

如何将条件内容移动到其他内容,如下所示:

if (!(conditionA = executeStepA()){}
else if (!(conditionB = executeStepB()){}
else if (!(conditionC = executeStepC()){}
else if (!(conditionD = executeStepD()){}
这确实解决了压痕问题。

rsaepe

赞同来自:

你这样做..

coverConditions();
executeThisFunctionInAnyCase();
function coverConditions()
 {
 bool conditionA = executeStepA();
 if (!conditionA) return;
 bool conditionB = executeStepB();
 if (!conditionB) return;
 bool conditionC = executeStepC();
 if (!conditionC) return;
 }
99次100,这是唯一的方法。 永远不要试图在计算机代码中做一些“棘手”的事情。
顺便说一句,我很确定以下是你想到的实际解决方案...... continue语句在算法编程中至关重要。 (同样,goto语句在算法编程中也很重要。) 在许多编程语言中,您可以这样做:
-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
int x = 69;
{
if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
}
NSLog(@"code g");
    }
(首先请注意:像这样的裸块是编写漂亮代码的关键和重要部分,特别是如果你正在处理“算法”编程。) 再一次,这正是你脑子里的那个,对吗?这是写它的美妙方式,所以你有很好的直觉。 然而,不幸的是,在当前版本的objective-c(旁白 - 我不知道Swift,对不起),有一个可行的功能,它检查封闭块是否是一个循环。 enter image description here

以下是你如何解决这个问题......
-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
int x = 69;
do{
if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
}while(false);
NSLog(@"code g");
    }
所以不要忘记..

do {} while(false); 只是意味着“做一次阻止”。 也就是说,写do{}while(false);和简单地写{}之间完全没有区别。 这现在可以完美地按照您的要求运行......这是输出... enter image description here 所以,你可能会在脑海中看到算法。你应该总是试着写下你脑子里的东西。 (特别是如果你不清醒,因为那是漂亮的时候!:)) 在“算法”项目中,这种情况发生了很多,在objective-c中,我们总是有一个宏...
#define RUNONCE while(false)
......那么你可以这样做......
-(void)_testKode
    {
    NSLog(@"code a");
    int x = 69;
do{
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    }RUNONCE
NSLog(@"code g");
    }
有两点: a,尽管目标-c检查一个继续语句所在的块的类型是愚蠢的,但是“打那个”很麻烦。所以这是一个艰难的决定。 b,在这个例子中,你应该缩进那个块吗?我在这样的问题上失眠了,所以我无法提出建议。 希望能帮助到你。

laut

赞同来自:

在阅读完所有答案后,我想提供一种新方法,在适当的情况下可以非常清晰和可读:状态模式。 如果将所有方法(executeStepX)打包到Object类中,它可以具有Attribute getState()

class ExecutionChain
{
    public:
        enum State
        {
          Start,
          Step1Done,
          Step2Done,
          Step3Done,
          Step4Done,
          FinalDone,
        };
        State getState() const;
void executeStep1();
        void executeStep2();
        void executeStep3();
        void executeStep4();
        void executeFinalStep();
    private:
        State _state;
};
这将允许您将执行代码展平为:
void execute
{
    ExecutionChain chain;
chain.executeStep1();
if ( chain.getState() == Step1Done )
    {
        chain.executeStep2();
    }
if ( chain.getState() == Step2Done )
    {
        chain.executeStep3();
    }
if ( chain.getState() == Step3Done )
    {
        chain.executeStep4();
    }
chain.executeFinalStep();
}
通过这种方式,它易于读取,易于调试,您具有清晰的流控制,并且还可以插入新的更复杂的行为(例如,仅在执行至少Step2时才执行特殊步骤)... 我的问题与其他方法,如ok = execute(); if(execute())是你的代码应该清晰可读,就像正在发生的事情的流程图一样。在流程图中,您将有两个步骤:1。执行2.基于结果的决策 因此,你不应该在if语句或类似内容中隐藏重要的重型方法,它们应该独立存在!

cet

赞同来自:

实际上有一种方法可以推迟C++中的操作:使用对象的析构函数。 假设您有权访问C++ 11:

class Defer {
public:
    Defer(std::function<void()> f): f_(std::move(f)) {}
    ~Defer() { if (f_) { f_(); } }
void cancel() { f_ = std::function<void()>(); }
private:
    Defer(Defer const&) = delete;
    Defer& operator=(Defer const&) = delete;
std::function<void()> f_;
}; // class Defer
然后使用该实用程序:
int foo() {
    Defer const defer{&executeThisFunctionInAnyCase}; // or a lambda
// ...
if (!executeA()) { return 1; }
// ...
if (!executeB()) { return 2; }
// ...
if (!executeC()) { return 3; }
// ...
return 4;
} // foo

id_aut

赞同来自:

内容太长未翻译

baut

赞同来自:

假设您不需要单独的条件变量,反转测试并使用else-falthrough作为“ok”路径将允许您获得更多垂直的if / else语句集:

bool failed = false;
// keep going if we don't fail
if (failed = !executeStepA())      {}
else if (failed = !executeStepB()) {}
else if (failed = !executeStepC()) {}
else if (failed = !executeStepD()) {}
runThisFunctionInAnyCase();
省略失败的变量会使代码过于模糊IMO。 在里面声明变量很好,不用担心= vs ==。
// keep going if we don't fail
if (bool failA = !executeStepA())      {}
else if (bool failB = !executeStepB()) {}
else if (bool failC = !executeStepC()) {}
else if (bool failD = !executeStepD()) {}
else {
     // success !
}
runThisFunctionInAnyCase();
这是模糊的,但紧凑:
// keep going if we don't fail
if (!executeStepA())      {}
else if (!executeStepB()) {}
else if (!executeStepC()) {}
else if (!executeStepD()) {}
else { /* success */ }
runThisFunctionInAnyCase();

bmagni

赞同来自:

在某些特殊情况下,虚拟继承树和虚方法调用可以处理您的决策树逻辑。

objectp -> DoTheRightStep();
我遇到过这种情况,当它像魔杖一样工作时。 当然,如果您的ConditionX可以一致地转换为“对象是A”条件,这是有道理的。

et_et

赞同来自:

[&]{
  bool conditionA = executeStepA();
  if (!conditionA) return; // break
  bool conditionB = executeStepB();
  if (!conditionB) return; // break
  bool conditionC = executeStepC();
  if (!conditionC) return; // break
}();
executeThisFunctionInAnyCase();
我们使用隐式引用捕获创建一个匿名lambda函数,然后运行它。其中的代码立即运行。 当它想要停止时,它只是returns。 然后,在它运行后,我们运行executeThisFunctionInAnyCase。 lambda中的returnbreak到块的结尾。任何其他类型的流量控制都可行。 例外是不可能的 - 如果你想抓住它们,请明确地做。如果抛出异常,请小心运行executeThisFunctionInAnyCase - 如果它可以在异常处理程序中抛出异常,则通常不希望运行executeThisFunctionInAnyCase,因为这会导致混乱(混乱将取决于语言)。 这种基于捕获的内联函数的一个很好的属性是你可以重构现有的代码。如果你的功能很长,那么将它分解成组件部分是一个好主意。 适用于更多语言的变体是:
bool working = executeStepA();
working = working && executeStepB();
working = working && executeStepC();
executeThisFunctionInAnyCase();
在那里你写每个短路的单独线路。可以在这些行之间注入代码,为您提供多个“在任何情况下”,或者您可以在执行步骤之间执行if(working) { /* code */ }以包含应该运行的代码,当且仅当您尚未获得保释时。 面对添加新的流量控制,这个问题的良好解决方案应该是强大的。 在C++中,更好的解决方案是将一个快速的scope_guard类放在一起:
#ifndef SCOPE_GUARD_H_INCLUDED_
#define SCOPE_GUARD_H_INCLUDED_
template<typename F>
struct scope_guard_t {
  F f;
  ~scope_guard_t() { f(); }
};
template<typename F>
scope_guard_t<F> scope_guard( F&& f ) { return {std::forward<F>(f)}; }
#endif
那么在有问题的代码中:
auto scope = scope_guard( executeThisFunctionInAnyCase );
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
并且scope的析构函数autolaly运行executeThisFunctionInAnyCase。每当您创建需要清理的非RAII资源时,您可以注入越来越多的“在范围的末尾”(给每个人提供不同的名称)。它也可以使用lambdas,因此您可以操作局部变量。 Fancier范围保护可以支持在析构函数中中止调用(使用bool保护),阻止/允许复制和移动,并支持可以从内部上下文返回的类型擦除的“可移植”范围保护。

tet

赞同来自:

这是我在C-whatever和Java中多次使用过的技巧:

do {
    if (!condition1) break;
    doSomething();
    if (!condition2) break;
    doSomethingElse()
    if (!condition3) break;
    doSomethingAgain();
    if (!condition4) break;
    doYetAnotherThing();
} while(FALSE);  // Or until(TRUE) or whatever your language likes
我更喜欢它,而不是嵌套ifs的清晰度,特别是在为每个条件准确的格式化时。

fet

赞同来自:

如果您不喜欢goto并且不喜欢do { } while (0);循环并喜欢使用C++,您也可以使用临时lambda来产生相同的效果。

& { // create a capture all lambda
  if (!executeStepA()) { return; }
  if (!executeStepB()) { return; }
  if (!executeStepC()) { return; }
}(); // and immediately call it
executeThisFunctionInAnyCase();

mut

赞同来自:

How to avoid “if” chains?
我还没有找到使用递归的答案。 (如果你听过这个,请立即停止我;) 我也没有找到支持来确定哪个if-clause失败了(想想多线程)...(我曾经在嵌入式系统中遇到过40个深链。)作为调试支持的一部分,我使用了bitset。
  // here I use 26 bits in a result bitset - you can use as many bits as you need
  typedef  std::bitset<26>  BitSet26;  
我的递归方法需要一些支持......来执行步骤选择:
  int executeStep(int i, bool& retVal)
  {
     int nxt = 0;
     switch (i)
     {
     case 'A': { retVal = executeStepA(); nxt = 'B'; } break;
     case 'B': { retVal = executeStepB(); nxt = 'C'; } break;
     case 'C': { retVal = executeStepC(); nxt = 'D'; } break;
     case 'D': { retVal = executeStepD(); nxt = 'I'; } break;
        // ...
     case 'I': { retVal = executeStepI(); nxt = 'J'; } break;
     case 'J': { retVal = executeStepJ(); nxt = 'R'; } break;
        // ...
     case 'R': { retVal = executeStepR(); nxt = 'S'; } break;
     case 'S': { retVal = executeStepS(); nxt = 'Z'; } break;
        // ...
     case 'Z': { retVal = executeStepZ(); nxt = 0;   } break;
default:  { retVal = false;          nxt = 0;   } break;
     }
     return(nxt);
  }
我认为上面的内容非常简单,但实际上我没有所有的“executeStepN()”方法,并且经常在相应的switch子句中安装它们包含的代码。 启动序列可能如下所示:
     BitSet26  b (0); // clear all bits
int retVal = tRecurse(b, 'A'); // launch the sequence, 
     //                                starting with step 'A'
executeThisFunctionInAnyCase();
这通过序列运行递归,没有if链。当步骤返回false时,递归终止。在tRecurse()结束时可以很容易地传回一个唯一的retVal(错误代码),但我想展示使用位模式来传递通过/失败信息。 递归看起来像这样:
  int tRecurse(BitSet26& b, int i)
  {
     int  retVal = 0;
     bool result = false;
     int     nxt = executeStep(i, result);
if (result) // if (previous step return true)
     {
        b['A'] = 1;  // map 'A' to 0 index
retVal = tRecurse(b, nxt); // recurse
} // else done, b initialized to 0
return(retVal);
  }
没有if-chains参与。好吧,我承认,我喜欢递归。 以下是启动递归并报告结果的示例:
  int foo(void)
  {
     BitSet26  b;
int retVal = tRecurse(b, 'A'); // launch the sequence, starting with step A
executeThisFunctionInAnyCase();
#ifndef  ndbg          
     { 
        // development diagnostics
        std::cout << "BitSet26: " <<  b.to_string() << std::endl;
        std::string c = "ZYXWVUTSRQPONMLKJIHGFEDCBA";
        std::cout << "          ";
        for (int i=0; i < 26; i++)
        {
           char kar = c[i];
           if(!b['A']) kar = ' ';
           std::cout << kar;
        }
std::cout << "   : PASS" << std::endl << "          ";
        for (int i=0; i < 26; i++)
        {
           char kar = static_cast<char>(tolower(c[i]));
           if(b['A']) kar = ' ';
           std::cout << kar;
        }
        std::cout << "   : Failed (or incomplete)" << std::endl;
     }
#endif
     return(retVal);
  }
用户输出是开发诊断,所以不要介意凌乱。 相反,请考虑您的错误处理如何根据找到的位模式决定恢复。 一个简单的例子,只有8个执行步骤成功,第9个'Z'失败。我想我应该只在bitset中使用9位......呃。
./dumy104 
A
B
C
D
...
I
J
...
R
S
...
Z
executeThisFunctionInAnyCase()
BitSet26: 00000001100000001100001111
                 SR       JI    DCBA   : PASS
          zyxwvut  qponmlk  hgfe       : Failed (Z) (or incomplete)
FINI

更新2014 07 30 忘了提到关键的想法: 您是否听说过将循环展开为优化技术? 在这里,我使用递归将if-then链转换为循环。当然,递归并不是编写循环的唯一方法。

xnobis

赞同来自:

Could break statement be used in some way?
也许不是最好的解决方案,但您可以将您的语句放在do .. while (0)循环中,并使用break语句而不是return

jenim

赞同来自:

你也可以这样做:

bool isOk = true;
std::vector<bool (*)(void)> funcs; //vector of function ptr
funcs.push_back(&executeStepA);
funcs.push_back(&executeStepB);
funcs.push_back(&executeStepC);
//...
//this will stop at the first false return
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();
通过这种方式,您可以获得最小的线性增长大小,每次调用+1行,并且可以轻松维护。
编辑:(谢谢@Unda)不是一个大粉丝,因为你失去了可见性IMO:
bool isOk = true;
auto funcs { //using c++11 initializer_list
    &executeStepA,
    &executeStepB,
    &executeStepC
};
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

overo

赞同来自:

为什么没有人提供最简单的解决方案? :d 如果所有函数都具有相同的签名,那么您可以这样做(对于C语言):

bool (*step[])() = {
    &executeStepA,
    &executeStepB,
    &executeStepC,
    ... 
};
for (int i = 0; i < numberOfSteps; i++) {
    bool condition = stepi;
if (!condition) {
        break;
    }
}
executeThisFunctionInAnyCase();
对于干净的C++解决方案,您应该创建一个包含execute方法的接口类,并将您的步骤包装在对象中 然后,上面的解决方案将如下所示:
Step *steps[] = {
    stepA,
    stepB,
    stepC,
    ... 
};
for (int i = 0; i < numberOfSteps; i++) {
    Step *step = steps[i];
if (!step->execute()) {
        break;
    }
}
executeThisFunctionInAnyCase();

icum

赞同来自:

做就是了

if( executeStepA() && executeStepB() && executeStepC() )
{
    // ...
}
executeThisFunctionInAnyCase();
就这么简单。
由于三个编辑每个都从根本上改变了问题(如果将修订版本重新计算为版本#1,则有四个),我将包含我正在回答的代码示例:
bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}
executeThisFunctionInAnyCase();

gomnis

赞同来自:

一种有趣的方式是使用异常。

try
{
    executeStepA();//function throws an exception on error
    ......
}
catch(...)
{
    //some error handling
}
finally
{
    executeThisFunctionInAnyCase();
}
如果你编写这样的代码,你会以某种方式走错方向。我不认为拥有这样的代码是“问题”,而是拥有如此混乱的“架构”。 提示:与您信任的经验丰富的开发人员讨论这些案例;-)

xporro

赞同来自:

您可以将所有if条件(根据需要进行格式化)放在自己的函数中,返回时执行executeThisFunctionInAnyCase()函数。 从OP中的基本示例中,可以将条件测试和执行分开;

void InitialSteps()
{
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
}
然后这样称呼;
InitialSteps();
executeThisFunctionInAnyCase();
如果C++ 11 lambdas可用(OP中没有C++ 11标签,但它们仍然是一个选项),那么我们可以放弃单独的函数并将其包装成lambda。
// Capture by reference (variable access may be required)
auto initialSteps = & {
  // any additional code
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  // any additional code
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  // any additional code
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
};
initialSteps();
executeThisFunctionInAnyCase();

phic

赞同来自:

看起来你想要从一个区块做你所有的电话。 正如其他人提出的那样,您应该使用while循环并使用break或使用return留下的新功能(可能更干净)。 即使功能退出,我个人也会放弃goto。调试时很难发现它们。 适用于您的工作流程的优雅替代方案是构建函数数组并对此进行迭代。

const int STEP_ARRAY_COUNT = 3;
bool (*stepsArray[])() = {
   executeStepA, executeStepB, executeStepC
};
for (int i=0; i<STEP_ARRAY_COUNT; ++i) {
    if (!stepsArrayi) {
        break;
    }
}
executeThisFunctionInAnyCase();

zet

赞同来自:

您可以使用&&(逻辑AND):

if (executeStepA() && executeStepB() && executeStepC()){
    ...
}
executeThisFunctionInAnyCase();
这将满足您的两个要求:
  • executeStep<X>()只应在前一个成功的情况下进行评估(此处称为short circuit evaluation)
  • executeThisFunctionInAnyCase()将在任何情况下执行

fillum

赞同来自:

已经有很多好的答案,但是他们中的大多数人似乎都在一定程度上(实际上很少)的灵活性进行权衡。不需要这种权衡的常见方法是添加状态/保持变量。当然,价格是追踪的一个额外价值:

bool ok = true;
bool conditionA = executeStepA();
// ... possibly edit conditionA, or just ok &= executeStepA();
ok &= conditionA;
if (ok) {
    bool conditionB = executeStepB();
    // ... possibly do more stuff
    ok &= conditionB;
}
if (ok) {
    bool conditionC = executeStepC();
    ok &= conditionC;
}
if (ok && additionalCondition) {
    // ...
}
executeThisFunctionInAnyCase();
// can now also:
return ok;

fearum

赞同来自:

这看起来像状态机,因为您可以使用state-pattern轻松实现它。 在Java中,它看起来像这样:

interface StepState{
public StepState performStep();
}
实施工作如下:
class StepA implements StepState{ 
    public StepState performStep()
     {
         performAction();
         if(condition) return new StepB()
         else return null;
     }
}
等等。然后你可以用以下方法替换大if条件:
Step toDo = new StepA();
while(toDo != null)
      toDo = toDo.performStep();
executeThisFunctionInAnyCase();

nipsa

赞同来自:

对于C++ 11及更高版本,一个很好的方法可能是实现类似于D's scope(exit)机制的范围退出系统。 实现它的一种可能方法是使用C++ 11 lambda和一些辅助宏:

template<typename F> struct ScopeExit 
{
    ScopeExit(F f) : fn(f) { }
    ~ScopeExit() 
    { 
         fn();
    }
F fn;
};
template<typename F> ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); };
#define STR_APPEND2_HELPER(x, y) x##y
#define STR_APPEND2(x, y) STR_APPEND2_HELPER(x, y)
#define SCOPE_EXIT(code)\
    auto STR_APPEND2(scope_exit_, __LINE__) = MakeScopeExit(&{ code })
这将允许您从函数中提前返回并确保您定义的任何清理代码始终在作用域退出时执行:
SCOPE_EXIT(
    delete pointerA;
    delete pointerB;
    close(fileC); );
if (!executeStepA())
    return;
if (!executeStepB())
    return;
if (!executeStepC())
    return;
这些宏真的只是装饰。 MakeScopeExit()可以直接使用。

uid

赞同来自:

鉴于功能:

string trySomething ()
{
    if (condition_1)
    {
        do_1();
        ..
            if (condition_k)
            {
                do_K();
return doSomething();
            }
            else
            {
                return "Error k";
            }
        ..
    }
    else
    {
        return "Error 1";
    }
}
通过反转验证过程,我们可以摆脱语法嵌套:
string trySomething ()
{
    if (!condition_1)
    {
        return "Error 1";
    }
do_1();
..
if (!condition_k)
    {
        return "Error k";
    }
do_K();
return doSomething ();
}

qfugit

赞同来自:

在C++中(问题是标记为C和C++),如果你不能改变函数来使用异常,你仍然可以使用异常机制,如果你写一个小辅助函数,如

struct function_failed {};
void attempt(bool retval)
{
  if (!retval)
    throw function_failed(); // or a more specific exception class
}
然后您的代码可以如下所示:
try
{
  attempt(executeStepA());
  attempt(executeStepB());
  attempt(executeStepC());
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}
executeThisFunctionInAnyCase();
如果你喜欢花哨的语法,你可以通过显式转换使其工作:
struct function_failed {};
struct attempt
{
  attempt(bool retval)
  {
    if (!retval)
      throw function_failed();
  }
};
然后你可以编写你的代码
try
{
  (attempt) executeStepA();
  (attempt) executeStepB();
  (attempt) executeStepC();
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}
executeThisFunctionInAnyCase();

hquae

赞同来自:

如果在各个步骤下移动条件,条件可以简化,这是一个c#伪代码, 我们的想法是使用编排而不是中央编排。

void Main()
{
    Request request = new Request();
    Response response = null;
// enlist all the processors
    var processors = new List<IProcessor>() {new StepA() };
var factory = new ProcessorFactory(processors);
// execute as a choreography rather as a central orchestration.
    var processor = factory.Get(request, response);
    while (processor != null)
    {
        processor.Handle(request, out response);
        processor = factory.Get(request, response); 
    }
// final result...
    //response
}
public class Request
{
}
public class Response
{
}
public interface IProcessor
{
    bool CanProcess(Request request, Response response);
    bool Handle(Request request, out Response response);
}
public interface IProcessorFactory
{
    IProcessor Get(Request request, Response response);
}
public class ProcessorFactory : IProcessorFactory
{
    private readonly IEnumerable<IProcessor> processors;
public ProcessorFactory(IEnumerable<IProcessor> processors)
    {
        this.processors = processors;
    }
public IProcessor Get(Request request, Response response)
    {
        // this is an iterator
        var matchingProcessors = processors.Where(x => x.CanProcess(request, response)).ToArray();
if (!matchingProcessors.Any())
        {
            return null;
        }
return matchingProcessors[0];
    }
}
// Individual request processors, you will have many of these...
public class StepA: IProcessor
{
    public bool CanProcess(Request request, Response response)
    {
        // Validate wether this can be processed -- if condition here
        return false;
    }
public bool Handle(Request request, out Response response)
    {
        response = null;
        return false;
    }
}

verror

赞同来自:

while(executeStepA() && executeStepB() && executeStepC() && 0);
executeThisFunctionInAnyCase();
即使其他函数没有完成,也必须在任何情况下执行executeThisFunctionInAnyCase()。 while声明:
while(executeStepA() && executeStepB() && executeStepC() && 0)
将执行所有函数,并不会循环作为一个明确的错误陈述。 这也可以在退出之前重试一定时间。

hvelit

赞同来自:

在我看来,函数指针是最好的方法。 之前已经提到过这种方法,但是我想更深入地探讨使用这种方法对付箭头类型代码的优点。 根据我的经验,这种if链发生在程序的某个动作的初始化部分。程序需要确保在尝试启动之前一切都很好。 在许多do stuff函数的常见情况下,某些东西可能会被分配,或者所有权可能会被更改。如果失败,您将需要撤消该过程。 假设您有以下3个功能:

bool loadResources()
{
   return attemptToLoadResources();
}
bool getGlobalMutex()
{
   return attemptToGetGlobalMutex();
}
bool startInfernalMachine()
{
   return attemptToStartInfernalMachine();
}
所有功能的原型将是:
typdef bool (*initializerFunc)(void);
因此,如上所述,您将使用push_back向指针添加到向量中,并按顺序运行它们。但是,如果程序在startInfernalMachine中失败,则需要手动返回互斥锁并卸载资源。如果在RunAllways函数中执行此操作,则会有一段时间。 可是等等!仿函数非常棒(有时候),您可以将原型更改为以下内容:
typdef bool (*initializerFunc)(bool);
为什么?那么,新功能现在看起来像:
bool loadResources(bool bLoad)
{
   if (bLoad)
     return attemptToLoadResources();
   else
     return attemptToUnloadResources();
}
bool getGlobalMutex(bool bGet)
{
  if (bGet)
    return attemptToGetGlobalMutex();
  else
    return releaseGlobalMutex();
}
...
所以现在,整个代码看起来像:
vector<initializerFunc> funcs;
funcs.push_back(&loadResources);
funcs.push_back(&getGlobalMutex);
funcs.push_back(&startInfernalMachine);
// yeah, i know, i don't use iterators
int lastIdx;
for (int i=0;i<funcs.size();i++)
{
   if (funcsi)
      lastIdx=i;
   else 
      break;
}
// time to check if everything is peachy
if (lastIdx!=funcs.size()-1)
{
   // sad face, undo
   for (int i=lastIdx;i>=0;i++)
      funcsi;
}
所以它肯定是自动清理你的项目的一步,并且已经过了这个阶段。 但是,实现有点尴尬,因为您需要反复使用此推回机制。如果你只有一个这样的地方,让我们说它没关系,但如果你有10个地方,有一些振荡的功能......不是那么有趣。 幸运的是,还有另一种机制可以让你做出更好的抽象:可变函数。 毕竟,你需要完成不同数量的功能。 可变函数看起来像这样:
bool variadicInitialization(int nFuncs,...)
{
    bool rez;
    int lastIdx;
    initializerFunccur;
    vector<initializerFunc> reverse;
    va_list vl;
    va_start(vl,nFuncs);
    for (int i=0;i<nFuncs;i++)
    {
        cur = va_arg(vl,initializerFunc);
        reverse.push_back(cur);
        rez= cur(true);
        if (rez)
            lastIdx=i;
        if (!rez)
            break;
    }
    va_end(vl);
if (!rez)
    {
for (int i=lastIdx;i>=0;i--)
        {
            reversei;
        }
    }
    return rez;
}
现在您的代码将减少(在应用程序中的任何位置)到此:
bool success = variadicInitialization(&loadResources,&getGlobalMutex,&startInfernalMachine);
doSomethingAllways();
因此,如果只有一个函数调用的列表,这样你可以做所有那些讨厌的事情,并确保当函数退出时你不会有任何初始化的残留。 你们的队友们非常感谢能够在1场比赛中完成100行代码。 可是等等! 还有更多! 箭头类型代码的一个主要特征是您需要具有特定的订单! 并且整个应用程序中的特定顺序需要相同(多线程死锁避免规则1:在整个应用程序中始终以相同的顺序使用互斥锁) 如果其中一个新手,只是按随机顺序制作功能怎么办?更糟糕的是,如果要求您将此公开给Java或C#,该怎么办? (是的,跨平台是一种痛苦) 幸运的是,有一种方法。 在要点中,这是我建议的:
  • 创建一个枚举,从第一个资源开始到最后一个
  • 定义一对从枚举中获取值并将其与函数指针
  • 配对的对
  • 把这些对放在一个向量中(我知道,我刚刚定义了一个地图的使用:),但我总是为小数字做矢量)
  • 将可变参数宏从将函数指针更改为整数(在java或C#中很容易公开;)))
  • 在可变参数函数中
  • ,对那些整数进行排序
  • 运行时,运行分配给该整数的函数。
最后,您的代码将确保以下内容:
  • 初始化的一行代码,无论有多少东西都可以,
  • 强制执行调用顺序:除非你(架构师)决定允许这样做,否则你不能在loadResources之前调用startInfernalMachine
  • 如果出现问题,则完全清理(考虑到您已正确取消初始化)
  • 更改整个应用程序中初始化的顺序意味着只更改枚举中的顺序

det

赞同来自:

一个简单的解决方案是使用条件布尔变量,并且可以反复重复使用同一个布尔变量,以便按顺序检查正在执行的步骤的所有结果:

    bool cond = executeStepA();
    if(cond) cond = executeStepB();
    if(cond) cond = executeStepC();
    if(cond) cond = executeStepD();
executeThisFunctionInAnyCase();
并不是事先没有必要这样做:bool cond = true; ...然后是if(cond)cond = executeStepA(); cond变量可以立即分配给executeStepA()的结果,从而使代码更简单,更易于阅读。 另一个更奇特但有趣的方法是这个(有些人可能会认为这是IOCCC的一个很好的候选人,但仍然):
    !executeStepA() ? 0 :
      !executeStepB() ? 0 :
      !executeStepC() ? 0 :
      !executeStepD() ? 0 : 1 ;
executeThisFunctionInAnyCase();
结果与OP发布的内容完全相同,即:
    if(executeStepA()){
        if(executeStepB()){
            if(executeStepC()){
                if(executeStepD()){
                }
            }
        }
    }
executeThisFunctionInAnyCase();

tin

赞同来自:

正如Rommik所提到的,你可以为此应用一个设计模式,但我会使用Decorator模式而不是策略,因为你想要链接调用。如果代码很简单,那么我会使用一个结构良好的答案来防止嵌套。但是,如果它很复杂或需要动态链接,那么Decorator模式是一个不错的选择。这是一个yUML class diagram

yUML class diagram 这是一个示例LinqPad C#程序:
void Main()
{
    IOperation step = new StepC();
    step = new StepB(step);
    step = new StepA(step);
    step.Next();
}
public interface IOperation 
{
    bool Next();
}
public class StepA : IOperation
{
    private IOperation _chain;
    public StepA(IOperation chain=null)
    {
        _chain = chain;
    }
public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step A success={0}", localResult);
//then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}
public class StepB : IOperation
{
    private IOperation _chain;
    public StepB(IOperation chain=null)
    {
        _chain = chain;
    }
public bool Next() 
    {   
        bool localResult = false;
//do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to false, 
            // to show breaking out of the chain
        localResult = false;
        Console.WriteLine("Step B success={0}", localResult);
//then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}
public class StepC : IOperation
{
    private IOperation _chain;
    public StepC(IOperation chain=null)
    {
        _chain = chain;
    }
public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step C success={0}", localResult);
        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}
关于设计模式的最佳书籍,恕我直言,是Head First Design Patterns

aet

赞同来自:

在这种情况下,旧学C程序员使用goto。它是goto的一种用法,它实际上受到Linux样式指南的鼓励,它被称为集中式函数退出:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanup;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;
result = 0;
cleanup:
    executeThisFunctionInAnyCase();
    return result;
}
有些人使用goto将身体包裹成一个循环并从中断开,但实际上两种方法都做同样的事情。如果仅在executeStepA()成功时需要进行其他清理,goto方法会更好:
int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanupPart;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;
result = 0;
cleanup:
    innerCleanup();
cleanupPart:
    executeThisFunctionInAnyCase();
    return result;
}
使用循环方法,在这种情况下最终会出现两级循环。

quo_et

赞同来自:

关于您当前的代码示例,基本上是问题#2,

[...block of code...]
bool conditionA = executeStepA();    
if (conditionA){
    [...block of code...]
    bool conditionB = executeStepB();
    if (conditionB){
        [...block of code...]
        bool conditionC = executeStepC();
        if (conditionC){
            ...other checks again...
        }
    }
}
executeThisFunctionInAnyCase();
除了将函数结果存储在变量中之外,这是典型的C代码。 如果布尔函数导致信号失败,则C++方式将使用异常,并将其编码为
struct Finals{ ~Finals() { executeThisFunctionInAnyCase(); } };
Finals finals;
// [...block of code...]
executeStepA();
// [...block of code...]
executeStepB();
// [...block of code...]
executeStepC();
//...other checks again...
但是,细节可能会因实际问题而有很大差异。 当我需要这样的一般性最终动作时,我经常使用一般范围保护类,而不是在现场定义自定义struct。对于C++ 98,范围保护是invented by Petru Marginean,然后使用临时生命周期扩展技巧。在C++ 11中,可以基于提供lambda表达式的客户端代码来简单地实现一般范围保护类。 在问题的最后,你提出了一个很好的C方法,即使用break语句:
for( ;; ) // As a block one can 'break' out of.
{
    // [...block of code...]
    if( !executeStepA() ) { break; }
    // [...block of code...]
    if( !executeStepB() ) { break; }
    // [...block of code...]
    if( !executeStepC() ) { break; }
    //...other checks again...
    break;
}
executeThisFunctionInAnyCase();
或者,对于C,将块中的代码重构为单独的函数,并使用return而不是break。这更加清晰,更通用,因为它支持嵌套循环或开关。但是,您询问了break。 与基于C++异常的方法相比,这依赖于程序员记住检查每个函数结果,并做正确的事情,这两者都是在C++中自动化的。

taut

赞同来自:

只需使用其他功能即可使您的第二个版本正常工作:

void foo()
{
  bool conditionA = executeStepA();
  if (!conditionA) return;
bool conditionB = executeStepB();
  if (!conditionB) return;
bool conditionC = executeStepC();
  if (!conditionC) return;
}
void bar()
{
  foo();
  executeThisFunctionInAnyCase();
}
使用深度嵌套的ifs(您的第一个变体)或打破“函数的一部分”的愿望通常意味着您需要额外的功能。

wearum

赞同来自:

代码中的IF / ELSE链不是语言问题,而是程序的设计。如果您能够重新计算或重写您的程序,我建议您查看设计模式(http://sourcemaking.com/design_patterns)以找到更好的解决方案。 通常,当你看到很多IF&否则在您的代码中,它是实现策略设计模式(http://sourcemaking.com/design_patterns/strategy/c-sharp-dot-net)或其他模式组合的机会。 我确定有一些替代方法可以写一个if / else的长列表,但是我怀疑它们会改变什么,除了链条看起来很漂亮(但是,旁观者眼中的美感仍然适用于代码也是:-))。您应该关注这样的事情(在6个月内,当我遇到新情况并且我不记得有关此代码的任何内容时,我能够轻松添加它吗?或者如果链条发生变化,速度和无错误怎么办?我会实施吗)

amodi

赞同来自:

那么,到目前为止已有50多个答案,没有人提到我在这种情况下通常做的事情! (即包含多个步骤的操作,但使用状态机或函数指针表会有点过分):

if ( !executeStepA() )
{
    // error handling for "A" failing
}
else if ( !executeStepB() )
{
    // error handling for "B" failing
}
else if ( !executeStepC() )
{
    // error handling for "C" failing
}
else
{
    // all steps succeeded!
}
executeThisFunctionInAnyCase();
优点:
  • 不会以巨大的缩进级别结束
  • 错误处理代码(可选)位于调用失败函数
  • 之后的行上
缺点:
  • 如果您的步骤不仅仅包含在单个函数调用中,那么可能会变得丑陋
  • 如果需要任何流程而不是“按顺序执行步骤,如果一个失败就中止”

nfugit

赞同来自:

另一种解决方案是通过宏观黑客定义成语。

 #define block for(int block = 0; !block; block++)
现在,可以使用break退出“块”,方法与for(;;)while()循环相同。例:
int main(void) {
block {
       if (conditionA) {
          // Do stuff A...
          break; 
       }
       if (conditionB) {
          // Do stuff B...
          break; 
       }
       if (conditionC) {
          // Do stuff C...
          break; 
       }
       else {
         // Do default stuff...
       }
    } /* End of "block" statement */
    /* --->   The "break" sentences jump here */
return 0;
} 
尽管有“for(;;)”结构,但“块”语句只执行一次 这个“块”可以用break句子退出。
因此,避免了if else if else if...句子的链 最多,最后一个else可以在“块”的末尾挂起,以处理“默认”情况。 此技术旨在避免典型和丑陋的do { ... } while(0)方法 在宏block中,定义了一个名为block的变量,该变量以这样的方式定义,即执行1次迭代。根据宏的替换规则,宏block定义内的标识符block不会被递归替换,因此block成为程序员无法识别的标识符,但内部很好地控制de“hidden”for(;;)循环。 而且:这些“块”可以嵌套,因为隐藏变量int block将具有不同的范围。

rtotam

赞同来自:

另一种方法 - do - while循环,即使之前已经提到过没有它的例子,它会显示它的样子:

do
{
    if (!executeStepA()) break;
    if (!executeStepB()) break;
    if (!executeStepC()) break;
    ...
break; // skip the do-while condition :)
}
while (0);
executeThisFunctionInAnyCase();
(嗯,已经有while循环的答案,但是do - while循环不会冗余地检查是否为真(在开始时),而是在末尾xD(尽管可以跳过)。

mneque

赞同来自:

这会有用吗?我认为这与您的代码相同。

bool condition = true; // using only one boolean variable
if (condition) condition = executeStepA();
if (condition) condition = executeStepB();
if (condition) condition = executeStepC();
...
executeThisFunctionInAnyCase();

ab_non

赞同来自:

为什么使用OOP?在伪代码中:

abstract class Abstraction():
   function executeStepA(){...};
   function executeStepB(){...};   
   function executeStepC(){...};
   function executeThisFunctionInAnyCase(){....}
   abstract function execute():
class A(Abstraction){
   function execute(){
      executeStepA();
      executeStepB();
      executeStepC();
   }
}
 class B(Abstraction){
   function execute(){
      executeStepA();
      executeStepB();
   }
}
class C(Abstraction){
     function execute(){
       executeStepA();
     }
}
这样你的ifs消失了 PLACEHOLDER FOR_CODE_1 通常,使用OOP可以避免使用ifs。

xsint

赞同来自:

如果执行函数失败而不是返回false,则抛出异常。那么你的调用代码可能如下所示:

try {
    executeStepA();
    executeStepB();
    executeStepC();
}
catch (...)
当然我假设在你的原始例子中,执行步骤只会在步骤内发生错误的情况下返回false?

homnis

赞同来自:

几个答案暗示了我多次看到和使用的模式,特别是在网络编程中。在网络堆栈中,通常存在很长的请求序列,其中任何一个都可能失败并将停止该过程。 常见的模式是使用do { } while (false); 我使用while(false)的宏来使其成为do { } once;常见的模式是:

do
{
    bool conditionA = executeStepA();
    if (! conditionA) break;
    bool conditionB = executeStepB();
    if (! conditionB) break;
    // etc.
} while (false);
这种模式相对容易阅读,并允许使用正确破坏的对象,并避免多次返回,使步进和调试更容易一些。

hquia

赞同来自:

假设所需的代码是我目前看到的:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}    
executeThisFunctionInAnyCase();
我会说正确的方法,因为它是最简单的阅读和最容易维护,会有较少的缩进程度,这是(目前)问题的既定目的。
// Pre-declare the variables for the conditions
bool conditionA = false;
bool conditionB = false;
bool conditionC = false;
// Execute each step only if the pre-conditions are met
conditionA = executeStepA();
if (conditionA)
    conditionB = executeStepB();
if (conditionB)
    conditionC = executeStepC();
if (conditionC) {
    ...
}
// Unconditionally execute the 'cleanup' part.
executeThisFunctionInAnyCase();
这避免了对gotos,异常,虚拟while循环或其他困难构造的任何需求,并且只需使用手头的简单工作即可。

dquia

赞同来自:

已经提到了假循环,但到目前为止我没有在给出的答案中看到以下技巧:您可以使用do { / ... / } while( evaulates_to_zero() );来实现双向早期断路器。使用break终止循环而不通过评估条件语句,而continue将调整条件语句。 如果你有两种终结方法,你可以使用它,其中一条路径必须比另一条路径做更多的工作:

#include <stdio.h>
#include <ctype.h>
int finalize(char ch)
{
    fprintf(stdout, "read a character: %c\n", (char)toupper(ch));
return 0;
}
int main(int argc, char *argv[])
{
    int ch;
    do {
        ch = fgetc(stdin);
        if( isdigit(ch) ) {
            fprintf(stderr, "read a digit (%c): aborting!\n", (char)ch);
            break;
        }
        if( isalpha(ch) ) {
            continue;
        }
        fprintf(stdout, "thank you\n");
    } while( finalize(ch) );
return 0;
}
执行此操作会提供以下会话协议:
dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
-
thank you
read a character: -
dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
a
read a character: A
dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
1
read a digit (1): aborting!

nsequi

赞同来自:

只是旁注;当if范围始终导致return(或循环中断)时,请不要使用else语句。这可以为您节省大量的缩进。

qanimi

赞同来自:

别。有时你需要复杂性。诀窍在于你是如何做到的。拥有“当条件存在时你做什么”可能会占用一些空间,使得if语句树看起来比实际大。因此,如果设置了条件,而不是做事情,只需将变量设置为该情况的特定值(枚举或数字,如10,014。在if树之后,然后有一个case语句,对于该特定值,请执行任何操作会在if树中完成。它会照亮树。 如果是x1    如果是x2       如果是x3           VAR1:= 100016;       万一    万一 万一 案件 VAR = 100016     做案例100016事......

rsequi

赞同来自:

很简单。

if ((bool conditionA = executeStepA()) && 
    (bool conditionB = executeStepB()) &&
    (bool conditionC = executeStepC())) {
   ...
}
executeThisFunctionInAnyCase();
这将保留布尔变量conditionA,conditionB和conditionC。

comnis

赞同来自:

为了改进Mathieu的C++ 11答案并避免因使用std::function而产生的运行时成本,我建议使用以下内容

template<typename functor>
class deferred final
{
public:
    template<typename functor2>
    explicit deferred(functor2&& f) : f(std::forward<functor2>(f)) {}
    ~deferred() { this->f(); }
private:
    functor f;
};
template<typename functor>
auto defer(functor&& f) -> deferred<typename std::decay<functor>::type>
{
    return deferred<typename std::decay<functor>::type>(std::forward<functor>(f));
}
这个简单的模板类将接受任何可以在没有任何参数的情况下调用的函子,并且没有任何动态内存分配,因此更好地符合C++的抽象目标而没有不必要的开销。附加功能模板用于简化模板参数推导的使用(不适用于类模板参数) 用法示例:
auto guard = defer(executeThisFunctionInAnyCase);
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
正如Mathieu的回答一样,这个解决方案是完全异常安全的,并且在所有情况下都会调用executeThisFunctionInAnyCase。如果executeThisFunctionInAnyCase本身抛出,则析构函数被隐式标记为noexcept,因此将发出对std::terminate的调用,而不是在堆栈展开期间引发异常。

hquas

赞同来自:

有一种很好的技术,不需要使用return语句(Itjax规定的方法)的附加包装函数。它使用了do while(0)伪循环。 while (0)确保它实际上不是循环但只执行一次。但是,循环语法允许使用break语句。

void foo()
{
  // ...
  do {
      if (!executeStepA())
          break;
      if (!executeStepB())
          break;
      if (!executeStepC())
          break;
  }
  while (0);
  // ...
}

yvero

赞同来自:

你可以使用“switch语句”

switch(x)
{
  case 1:
    //code fires if x == 1
    break;
  case 2:
    //code fires if x == 2
    break;
...
default:
    //code fires if x does not match any case
}
相当于:
if (x==1)
{
  //code fires if x == 1
}
else if (x==2)
{
  //code fires if x == 2
}
...
else
{
  //code fires if x does not match any of the if's above
}
但是,我认为没有必要避免if-else-chains。 switch语句的一个限制是它们只测试完全相等;那就是你无法测试“case x< 3”---在C++中抛出一个错误&在C中它可能有效,但表现出意想不到的方式,这比抛出错误更糟糕,因为你的程序会以意想不到的方式发生故障。

oiusto

赞同来自:

正如@Jefffrey所说,你可以在几乎所有语言中使用条件短路功能,我个人不喜欢超过2个条件的条件语句(超过单个&&||),只是风格问题。这段代码也是一样的(并且可能会编译相同的代码),它对我来说看起来更清晰。只要executeStepX()中的每个函数都返回一个可以强制转换为true的值(如果下一个语句是),则不需要花括号,中断,返回,函数,lambdas(仅限c ++ 11),对象等。执行或false否则。

if (executeStepA())
 if (executeStepB())
  if (executeStepC())
   //...
    if (executeStepN()); // <-- note the ';'
executeThisFunctionInAnyCase();
每当任何函数返回false时,都不会调用下一个函数。 我喜欢@Mayerz的答案,因为你可以在运行时改变要调用的函数(及其顺序)。这种感觉就像observer pattern,你有一组订阅者(函数,对象,等等),只要满足给定的任意条件,就会调用和执行这些订阅者。在许多情况下这可能是过度杀戮,所以明智地使用它:)