Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

Hi all

 

Which is more effective/appropriate to use, an if statement or a case statement?

 

Mike55.

A Client refers to the person who incurs the development cost.

A Customer refers to the person that pays to use the product.

------

My software never has bugs. It just develops random features. (Mosabama vbforums.com)

  • Administrators
Posted
Depends on the situation - a select case is often cleaner and more maintainable when you have one condition but several possibilities, if .. else if .. else allows you to have different and more complex decision making logic for each if / else if.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted
For simple matching, is switch-case more efficient than a long list of if-elses? I think I read somewhere that it is compiled internally to a hashtable, or something similar.
Posted (edited)

Select Case compilation

 

It depends on the nature of the Select Case (or switch in C#) block. Those which have sequential numeric conditions compile to a special switch instruction containing the list of branch locations. Those which have non-sequential or non-numeric conditions tend to compile to a series of conditional statements, as if they were each if/else conditions. For example:

 

string s = "hello world";
int i;

switch (s[1]) { //What is the second letter?
   case 'h': i = 1; break;
   case 'e': i = 2; break;
   case 'l': i = 3; break;
   case 'o': i = 4; break;
   default: i = 5; break;
}

switch (i) {
   case 1: s = "h"; break;
   case 2: s = "e"; break;
   case 3: s = "l"; break;
   case 4: s = "o"; break;
   default: s = string.Empty; break;
}

 

The first switch block, being non-sequential, compiles into a series of conditional tests:

 

  IL_0008:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)
 IL_000d:  stloc.2
 IL_000e:  ldloc.2
 IL_000f:  ldc.i4.s   104
 IL_0011:  bgt.s      IL_001f    ;greater than 'h'? (special conditional)
 IL_0013:  ldloc.2
 IL_0014:  ldc.i4.s   101
 IL_0016:  beq.s      IL_002f    ;conditional branch (e)
 IL_0018:  ldloc.2
 IL_0019:  ldc.i4.s   104
 IL_001b:  beq.s      IL_002b    ;conditional branch (h)
 IL_001d:  br.s       IL_003b
 IL_001f:  ldloc.2
 IL_0020:  ldc.i4.s   108
 IL_0022:  beq.s      IL_0033    ;conditional branch (l)
 IL_0024:  ldloc.2
 IL_0025:  ldc.i4.s   111
 IL_0027:  beq.s      IL_0037    ;conditional branch (o)
 IL_0029:  br.s       IL_003b
 IL_002b:  ldc.i4.1              ;case 'h'
 IL_002c:  stloc.1
 IL_002d:  br.s       IL_003d
 IL_002f:  ldc.i4.2              ;case 'e'
 IL_0030:  stloc.1
 IL_0031:  br.s       IL_003d
 IL_0033:  ldc.i4.3              ;case 'l'
 IL_0034:  stloc.1
 IL_0035:  br.s       IL_003d
 IL_0037:  ldc.i4.4              ;case 'o'
 IL_0038:  stloc.1
 IL_0039:  br.s       IL_003d
 IL_003b:  ldc.i4.5              ;default
 IL_003c:  stloc.1

 

The second switch block, having sequential conditions, compiles to a switch instruction:

 

  IL_0042:  switch     ( 
                       IL_0059,
                       IL_0061,
                       IL_0069,
                       IL_0071)  ;single branching instruction
 IL_0057:  br.s       IL_0079
 IL_0059:  ldstr      "h"        ;case 1
 IL_005e:  stloc.0
 IL_005f:  br.s       IL_007f
 IL_0061:  ldstr      "e"        ;case 2
 IL_0066:  stloc.0
 IL_0067:  br.s       IL_007f
 IL_0069:  ldstr      "l"        ;case 3
 IL_006e:  stloc.0
 IL_006f:  br.s       IL_007f
 IL_0071:  ldstr      "o"        ;case 4
 IL_0076:  stloc.0
 IL_0077:  br.s       IL_007f
 IL_0079:  ldsfld     string [mscorlib]System.String::Empty ;default
 IL_007e:  stloc.0

 

For a series of string comparisons, the first style is used, with a series of calls to op_Equality and then branching from there:

 

  IL_008b:  ldstr      "foo"
 IL_0090:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                string)
 IL_0095:  brtrue.s   IL_00c3
 IL_0097:  ldloc.s    CS$0$0002
 IL_0099:  ldstr      "bar"
 IL_009e:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                string)
 IL_00a3:  brtrue.s   IL_00ca
 IL_00a5:  ldloc.s    CS$0$0002
 IL_00a7:  ldstr      "pin"
 IL_00ac:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                string)
 IL_00b1:  brtrue.s   IL_00d1
 IL_00b3:  ldloc.s    CS$0$0002
 IL_00b5:  ldstr      "pop"
 IL_00ba:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                string)
 IL_00bf:  brtrue.s   IL_00d8

 

Good luck :)

Edited by MrPaul
Never trouble another for what you can do for yourself.
Posted

What about a case like this:

switch (integer)
{
   case 1:
       break;
   case 2:
       break;
   case 3:
       break;
   case 4:
       break;
   case 9:
       break;
   case 10:
       break;
   case 11:
       break;
}

Posted (edited)

Jump targets

 

In that case the switch instruction is used with 11 jump targets (1 to 11), and the jumps for values 5, 6, 7, and 8 all branch to the end of the code block.

 

If, however, the jump conditions are 1, 2, 3, 4 and 999, 1000, 1001 (large gap in values) then the compiler generates 2 switch blocks (1, 2, 3, 4 and 999, 1000, 1001). The values 999, 1000 and 1001 fail the first switch, it then subtracts 999 from the number and enters the second switch block, where they may succeed. Switch blocks are 0-based.

Edited by MrPaul
Never trouble another for what you can do for yourself.
  • Leaders
Posted
I believe the compiler has a little flexibility. If the values are mostly sequential and/or all within a small range the compiler will use a "calculated goto," the list of branches mentioned by MrPaul. I was under the impression that string-based select cases used a hashtable. The best way to find out, though, would be to compile code and disassemble it with reflector.
[sIGPIC]e[/sIGPIC]
Posted

Hashtables

 

I believe the compiler has a little flexibility.

 

Exactly. Personally I would suggest worrying less about the efficiency and simply choosing constructs which make the most sense and are easiest to read.

 

I was under the impression that string-based select cases used a hashtable. The best way to find out, though, would be to compile code and disassemble it with reflector.

 

I just did a quick test and it appears that if there are fewer than 6 case labels, then the compiler generates a series of conditional tests. With 6 or more case labels, a Dictionary<string,int32> is used to associate case values with their 'index' in the case block (0-based). This is then used in a switch instruction which does the branching. However, this is unlikely to be a set-in-stone rule so you should not rely on it. Compiler code optimizations can be quite advanced.

 

  IL_00ea:  volatile.
 IL_00ec:  ldsfld     class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{F321BCDB-EE97-4BEC-9C3A-65BCE7603F07}'::'$$method0x6000005-1'
 IL_00f1:  ldloc.s    CS$0$0002
 IL_00f3:  ldloca.s   CS$0$0003
 IL_00f5:  call       instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::TryGetValue(!0,
                                                                                                                       !1&)
 IL_00fa:  brfalse.s  IL_014d    ;no match - go to default
 IL_00fc:  ldloc.s    CS$0$0003
 IL_00fe:  switch     ( 
                       IL_011d,
                       IL_0125,
                       IL_012d,
                       IL_0135,
                       IL_013d,
                       IL_0145)

 

:cool:

Never trouble another for what you can do for yourself.
  • *Experts*
Posted

I generally wouldn't worry about performance between if and case, unless your app really needs that level of tweaking. For me, I rarely need a switch unless I've got some kind of factory method that's making decisions.

 

Two other thoughts:

1. Switch statements might be a bad smell (from refactoring) if you have duplicated switches in code.

2. See Code Complete 2's Checklist

 

Since CC2 doesn't provide a clear "which to use and when" guide, I'm guessing the choice to if or case falls into one of two categories: first, decide if there's a reason to NOT use one or the other (the checklist should help) and then just go by personal/professional opinion.

 

-ner

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
Posted
I was under the impression that it did work by table lookup, not because I disassembled the compiled code, but because of how the debugger worked. In a switch, it jumps to the correct case, but by if-else-if-else, if tested each one. Obviously that may not mean anything when it comes down to execution speed, it does make it a little less grating when stepping through code though.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...