开关案例编程实践

enum SQLErrorCode{ OK = 0, PARTIAL_OK = 1, SOMEWHAT_OK = 2, NOT_OK = 3, }; 

代码1:

 int error = getErrorCode(); if((error == SQLErrorCode.PARTIAL_OK) || (error == SQLErrorCode.SOMEWHAT_OK) || (error == SQLErrorCode.NOT_OK) || (error < 0)) callFunction1(); else callFunction2(); 

代码2:

 switch(error){ case SQLErrorCode.PARTIAL_OK: callFunction1(); break; case SQLErrorCode.SOMEWHAT_OK: callFunction1(); break; case SQLErrorCode.NOT_OK: callFunction1(); break; default: callFunction2(); break; } 

我应该选择哪种方法。 就性能而言,应该没有太大的区别。 如何在switch case中处理错误<0条件。

编辑:乔尔的解决方案:

 switch(error) { case SQLErrorCode.PARTIAL_OK: case SQLErrorCode.SOMEWHAT_OK: case SQLErrorCode.NOT_OK: callFunction1(); break; case SQLErrorCode.OK: callFunction2(); break; default: // error < 0 is handled callFunction1(); break; } 

问:错误<0被处理。 如果我必须处理错误的其他数字,这些数字不属于此处包括默认的任何情况。

对于这么少的情况并不重要,但是对于整数来说switch实际上更快:它可以并且经常被实现为跳转表而不是一系列条件检查。

作为比较,不同案例的数量为10:

 enum SQLErrorCode{ CODE0 = 0, CODE1 = 1, CODE2 = 2, CODE3 = 3, CODE4 = 4, CODE5 = 5, CODE6 = 6, CODE7 = 7, CODE8 = 8, CODE9 = 9 }; enum SQLErrorCode getErrorCode(); void run() { int error = getErrorCode(); #ifdef CASE1 if((error == CODE0) || (error == CODE1) || (error == CODE2) || (error == CODE3) || (error == CODE4) || (error == CODE5) || (error == CODE6) || (error == CODE7) || (error == CODE8) || (error == CODE9) || (error < 0)) callFunction1(); else callFunction2(); #endif #ifdef CASE2 switch(error) { case CODE0: callFunction1(); break; case CODE1: callFunction1(); break; case CODE2: callFunction1(); break; case CODE3: callFunction1(); break; case CODE4: callFunction1(); break; case CODE5: callFunction1(); break; case CODE6: callFunction1(); break; case CODE7: callFunction1(); break; case CODE8: callFunction1(); break; case CODE9: callFunction1(); break; default: callFunction2(); break; } #endif 

}

现在看看使用GCC在Linux上构建时,通过执行第一个案例而不是第二个案例生成的程序集。

如果你看一下程序集,你会看到一个重要的区别(对于较大的语句):系列的|| s(或者if如果你这样做的话,那么)是一次一个分支。 switch变成了一个大表:它占用了更多的代码,但可能意味着它可以在一次跳转中处理。

(顺便说一句,我们在这里谈论C?不是C#?你的代码不会编译:在C枚举器中不使用枚举名作为前缀。所以它是没有SQLErrorCode.PARTIAL_OK SQLErrorCode.

代码1cc -DCASE1 -s switch.s switch.c

  .file "1241256.c" .text .globl run .type run, @function run: pushl %ebp movl %esp, %ebp subl $24, %esp call getErrorCode movl %eax, -4(%ebp) cmpl $0, -4(%ebp) je .L2 cmpl $1, -4(%ebp) je .L2 cmpl $2, -4(%ebp) je .L2 cmpl $3, -4(%ebp) je .L2 cmpl $4, -4(%ebp) je .L2 cmpl $5, -4(%ebp) je .L2 cmpl $6, -4(%ebp) je .L2 cmpl $7, -4(%ebp) je .L2 cmpl $8, -4(%ebp) je .L2 cmpl $9, -4(%ebp) je .L2 cmpl $0, -4(%ebp) jns .L13 .L2: call callFunction1 jmp .L15 .L13: call callFunction2 .L15: leave ret .size run, .-run .ident "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)" .section .note.GNU-stack,"",@progbits 

代码2cc -DCASE2 -s switch.s switch.c

  .text .globl run .type run, @function run: pushl %ebp movl %esp, %ebp subl $24, %esp call getErrorCode movl %eax, -4(%ebp) cmpl $9, -4(%ebp) ja .L2 movl -4(%ebp), %eax sall $2, %eax movl .L13(%eax), %eax jmp *%eax .section .rodata .align 4 .align 4 .L13: .long .L3 .long .L4 .long .L5 .long .L6 .long .L7 .long .L8 .long .L9 .long .L10 .long .L11 .long .L12 .text .L3: call callFunction1 jmp .L15 .L4: call callFunction1 jmp .L15 .L5: call callFunction1 jmp .L15 .L6: call callFunction1 jmp .L15 .L7: call callFunction1 jmp .L15 .L8: call callFunction1 jmp .L15 .L9: call callFunction1 jmp .L15 .L10: call callFunction1 jmp .L15 .L11: call callFunction1 jmp .L15 .L12: call callFunction1 jmp .L15 .L2: call callFunction2 .L15: leave ret .size run, .-run .ident "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)" .section .note.GNU-stack,"",@progbits 

如果没有表达对最佳选择的偏好,这是另一种可能性:

 switch(error){ case SQLErrorCode.PARTIAL_OK: case SQLErrorCode.SOMEWHAT_OK: case SQLErrorCode.NOT_OK: callFunction1(); break; default: callFunction2(); break; } 

为什么不…

 switch(error) { case SQLErrorCode.PARTIAL_OK: case SQLErrorCode.SOMEWHAT_OK: case SQLErrorCode.NOT_OK: callFunction1(); break; case SQLErrorCode.OK: callFunction2(); break; default: if (error < 0) callFunction1(); else callFunction2(); break; } 

比你的开关更容易编写,比你的更容易阅读。 然而它仍然处理错误<0。

编辑:

理查德提出了一个很好的观点。 我已编辑处理已知范围之外的正面和负面错误。

假设getErrorCode()返回一个枚举值或小于0的值,那么怎么样

 int error = getErrorCode(); if (error == SQLErrorCode.OK) callFunction2(); // Good path else callFunction1(); // Error / not good enough path 

显然,如果你的代码需要在error > 3 callFunction2() ,那么这不起作用。

您还可以编写一个函数来确定OK错误是什么错误代码是:

 bool isOK(int code) { return code == SQLErrorCode.OK; } 

你的代码可能变成:

 if (isOk(getErrorCode())) { callFunction2; } else { callFunction1; } 

我有一段时间没有接触过C,但是它没有落空吗? 所以你可以写第二个夹头……

 switch(error){ case SQLErrorCode.PARTIAL_OK: case SQLErrorCode.SOMEWHAT_OK: case SQLErrorCode.NOT_OK: callFunction1(); break; default: callFunction2(); break; 

}

只要你有多种方法可以获得相同的效果,就会造成混乱。 因为在交换机版本中,你有不同的SqlErrorCode.PARTIAL_OKSqlErrorCode.SOMEWHAT_OK ,这意味着它们有不同的处理。 需要一些研究才能看到正在发生的事情(并且它与你的if语句处理不完全兼容,这可能意味着它让你感到困惑)。

在这种情况下,我使用if语句,因为这个想法是使用一个或另一个函数。