Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A "Function lookup table" in place of switches

I came across some code recently that replaces the use of switches by hard-coding a

Dictionary<string (or whatever we would've been switching on), Func<...>> 

and where ever the switch would've been, it instead does dict["value"].Invoke(...). The code feels wrong in some way, but at the same time, the methods do look a bit cleaner, especially when there's many possible cases. I can't give any rationale as to why this is good or bad design so I was hoping someone could give some reasons to support/condemn this kind of code. Is there a gain in performance? Loss of clarity?

Example:

public class A {
    ...
    public int SomeMethod(string arg){
        ...
        switch(arg) {
            case "a": do stuff; break;
            case "b": do other stuff; break;
            etc.
        }
        ...
    }
    ...
}

becomes

public class A {

    Dictionary<string, Func<int>> funcs = new Dictionary<string, Func<int>> {
        { "a", () => 0; },
        { "b", () => DoOtherStuff(); }
        ... etc.
    };

    public int SomeMethod(string arg){
        ...
        funcs[arg].Invoke();
        ...
    }
    ...
}
like image 778
Roly Avatar asked Mar 10 '11 15:03

Roly


People also ask

What is a function lookup table in JavaScript?

Function lookup table. A switch statement can run a block of code when a value equals a specific string. But an object with function values can do the same thing. In this benchmark, we test an object with function values.

Is a lookup table better than a switch in JavaScript?

Thus it appears in JavaScript a switch is the best option for max performance. Switch Some considerations. A lookup table may lead to clearer code, and often lookup time is not an important part of web application performance. But on a low level, the switch appears superior.

What is an example of a lookup table?

Lookup table example. A lookup table of functions lets us encode branches in memory. We put a function object in each index of an array. Array Then: We invoke functions based on an array element access. We simply call the array element as a function.

How do you read the function address of a switch function?

Lookup-table: Reading the function address is a data-access. With all implications mentioned above. A switch otoh uses a special "table-lookup" instruction which uses code-space data right behind the instruction. So the first entries are possibly already prefetched. Other entries don't break the prefetch.


2 Answers

Advantages:

  1. You can change the behaviour at runtime of the "switch" at runtime
  2. it doesn't clutter the methods using it
  3. you can have non-literal cases (ie. case a + b == 3) with much less hassle

Disadvantages:

  1. All of your methods must have the same signature.
  2. You have a change of scope, you can't use variables defined in the scope of the method unless you capture them in the lambda, you'll have to take care of redefining all lambdas should you add a variable at some point
  3. you'll have to deal with non-existant indexes specifically (similar to default in a switch)
  4. the stacktrace will be more complicated if an unhandled exception should bubble up, resulting in a harder to debug application

Should you use it? It really depends. You'll have to define the dictionary at some place, so the code will be cluttered by it somewhere. You'll have to decide for yourself. If you need to switch behaviour at runtime, the dictionary solution really sticks out, especially, if the methods you use don't have sideeffects (ie. don't need access to scoped variables).

like image 192
Femaref Avatar answered Oct 04 '22 06:10

Femaref


For several reasons:

  1. Because doing it this way allows you to select what each case branch will do at runtime. Otherwise, you have to compile it in.
  2. What's more, you can also change the number of branches at runtime.
  3. The code looks much cleaner especially with a large number of branches, as you mention.

Why does this solution feel wrong to you? If the dictionary is populated at compile time, then you certainly don't lose any safety (the delegates that go in certainly have to compile without error). You do lose a little performance, but:

  1. In most cases the performance loss is a non-issue
  2. The flexibility you gain is enormous
like image 24
Jon Avatar answered Oct 04 '22 04:10

Jon