开关案例编程实践
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.
)
代码1 : cc -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
代码2 : cc -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_OK
和SqlErrorCode.SOMEWHAT_OK
,这意味着它们有不同的处理。 需要一些研究才能看到正在发生的事情(并且它与你的if语句处理不完全兼容,这可能意味着它让你感到困惑)。
在这种情况下,我使用if语句,因为这个想法是使用一个或另一个函数。