简体   繁体   中英

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();
        ...
    }
    ...
}

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).

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

Jon has a couple good answers. Here are some more:

  • Whenever you need a new case in a switch, you have to code it in to that switch statement. That requires opening up that class (which previously worked just fine), adding the new code, and re-compiling and re-testing that class and any class that used it. This violates a SOLID development rule, the Open-Closed Principle (classes should be closed to modification, but open to extension). By contrast, a Dictionary of delegates allows delegates to be added, removed, and swapped out at will, without changing the code doing the selecting.
  • Using a Dictionary of delegates allows the code to be performed in a condition to be located anywhere, and thus given to the Dictionary from anywhere. Given this freedom, it's easy to turn the design into a Strategy pattern where each delegate is provided by a unique class that performs the logic for that case. This supports encapsulation of code and the Single Responsibility Principle (a class should do one thing, and should be the only class responsible for that thing).

No one has said anything yet about what I believe to be the single biggest drawback of this approach.

It's less maintainable.

I say this for two reasons.

  1. It's syntactically more complex.
  2. It requires more reasoning to understand.

Most programmers know how a switch statement works. Many programmers have never seen a Dictionary of functions.

While this might seem like an interesting and novel alternative to the switch statement and may very well be the only way to solve some problems, it is considerably more complex. If you don't need the added flexibility you shouldn't use it.

If there are more number of possible cases then it is good idea to replace Switch Statement with the strategy pattern , See this .

Applying Strategy Pattern Instead of Using Switch Statements

Convert your A class to a partial class, and create a second partial class in another file with just the delegate dictionary in it.

Now you can change the number of branches, and add logic to your switch statement without touching the source for the rest of your class.

(Regardless of language) Performance-wise, where such code exists in a critical section, you are almost certainly better off with a function look-up table.

The reason is that you eliminate multiple runtime conditionals (the longer your switch, the more comparisons there will be) in favour of simple array indexing and function call.

The only performance downside is you've introduced the cost of a function call. This will typically be preferable to said conditionals. Profile the difference; YMMV.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM