切换案例编程实践

Posted

技术标签:

【中文标题】切换案例编程实践【英文标题】:switch case programming practice 【发布时间】:2009-08-06 20:32:54 【问题描述】:
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 中的错误

编辑: 乔尔的解决方案:

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;

Q. 错误

【问题讨论】:

你在说C吗?不是 C#? SQLErrorCode. 前缀不合法 C. 这是标记为 C,但 C 不允许 enumtype.enumval。你的意思是用 C# 标记它吗? 大部分 switch 示例都在改变原代码的含义。它们的含义更像是:“if( .... || (error NOT_OK) )”。如果这确实是您想要的,那么只需:if (error != 0)。 我已经修改了我的答案以供您编辑。新的应该处理所有的情况。基本概念:使用特定案例语句处理已知案例以及默认情况下的所有其他内容。 【参考方案1】:

没有表达对哪个最好的偏好,这里有另一种可能性:

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

【讨论】:

比原来的开关功能好多了。 糟糕!我错过了:|我的错! 嘿,我们都可以搞砸一次......但下一次......你已经完成了:) 这是我的偏好。相关问题:***.com/questions/188461/… 好的,这个很“优雅”,但我不会使用这种方法。为什么,你可能会问?当您突然想用另一个函数处理 SOMEWHAT_OK 时,这可能非常危险。在这里很容易打破整个逻辑。我没有拒绝这个答案,但大卫的评论与我的想法一致。【参考方案2】:

对于这么少的情况并不重要,但 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/else,如果您这样做)是一系列分支一次一个。 switch 变成了一张大桌子:它占用了更多的代码,但可能意味着它可以一次处理。

(顺便说一句,我们在这里讨论的是 C 对吗?不是 C#?你的代码不会编译:在 C 中,枚举器不使用枚举名称作为前缀。所以它是 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

【讨论】:

这正是我想要的! 是的,这是默认的(显示的命令行是我使用的)。【参考方案3】:

为什么不...

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;

比你的 switch 更容易写,比你的 if 更容易阅读。然而它仍然处理错误

编辑:

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

【讨论】:

我投了这个票,虽然我自己更喜欢 break 与单词 case 对齐。 当 'error' 是一个正值而不是枚举中列出的值之一时会发生什么?例如10?在原始代码中,将调用“callFunction2”,但是在您的代码中调用“callFunction1”。这种行为可能不是“错误的”,但是它与原始代码不同。【参考方案4】:

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

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

显然,如果您的代码需要在error &gt; 3 上使用callFunction2(),那么这是行不通的。

【讨论】:

我正要提出同样的建议。【参考方案5】:

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

bool isOK(int code)

  return code == SQLErrorCode.OK;

你的代码可能变成:

if (isOk(getErrorCode()))

  callFunction2;

else

  callFunction1;

【讨论】:

+1:这是唯一一个似乎已经掌握了 switch 语句对于说“!= OK”是矫枉过正的答案。【参考方案6】:

我有一段时间没碰C了,但它不是掉线了吗?所以你可以这样写第二个夹头......

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

你在这里很真实。我很困惑我应该更喜欢哪一个。

以上是关于切换案例编程实践的主要内容,如果未能解决你的问题,请参考以下文章

P2P网络编程-3-案例实践:PubSub

Python编程:从入门到实践

HDFS编程实践(Hadoop3.1.3)

机器人编程趣味实践06-程序(节点)

Nginx 实践案例(源码编译安装方式):利用LNMP搭建wordpress站点

机器人微控制器编程(CoCube)-强化实践