“函数查找表”代替开关

发布于 2024-10-21 09:27:18 字数 948 浏览 6 评论 0原文

我最近遇到了一些代码,通过硬编码 a 来替换开关的使用

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

,并且无论开关在哪里,它都会改为 dict["value"].Invoke(...)。 代码在某种程度上感觉是错误的,但同时,这些方法看起来确实有点干净,特别是当有很多可能的情况时。我无法给出任何理由来解释为什么这是好的或坏的设计,所以我希望有人能给出一些理由来支持/谴责这种代码。性能上有提升吗?失去清晰度?

示例:

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

变成

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

一桥轻雨一伞开 2024-10-28 09:27:18

优点:

  1. 您可以在运行时更改“开关”的运行时行为
  2. ,不会使使用它的方法变得混乱,
  3. 您可以有非文字情况(即case a + b == 3)麻烦少得多

缺点:

  1. 所有方法都必须具有相同的签名。
  2. 在某个时刻添加变量,则必须重新定义所有 lambda
  3. 您的范围发生了变化,您不能使用在方法范围内定义的变量,除非您在 lambda 中捕获它们,如果您 必须专门处理不存在的索引(类似于 switch 中的 default),
  4. 如果未处理的异常冒泡,堆栈跟踪将变得更加复杂,从而导致更难调试应用程序

你应该使用它吗?这确实取决于。您必须在某个地方定义字典,因此代码会在某个地方被它弄乱。你必须自己决定。如果您需要在运行时切换行为,那么字典解决方案确实很突出,特别是如果您使用的方法没有副作用(即不需要访问作用域变量)。

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

铜锣湾横着走 2024-10-28 09:27:18

有几个原因:

  1. 因为这样做可以让您选择每个 case 分支在运行时执行的操作。否则,您必须编译它。
  2. 此外,您还可以在运行时更改分支的数量
  3. 正如您提到的,代码看起来更干净,尤其是有大量分支。

为什么您觉得这个解决方案不对?如果字典是在编译时填充的,那么您当然不会失去任何安全性(进入的委托肯定必须编译而不会出现错误)。您确实会损失一点性能,但是:

  1. 在大多数情况下,性能损失不是问题
  2. 您获得的灵活性是巨大的

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
千寻… 2024-10-28 09:27:18

乔恩有几个很好的答案。以下是更多内容:

  • 每当您需要在 switch 中使用新的 case 时,您都必须将其编码到该 switch 语句中。这需要打开该类(以前工作得很好),添加新代码,然后重新编译和重新测试该类以及使用它的任何类。这违反了 SOLID 开发规则,即开放-封闭原则(类应该对修改封闭,但对扩展开放)。相比之下,委托字典允许随意添加、删除和交换委托,而无需更改执行选择的代码。
  • 使用委托字典允许代码在位于任何地方的条件下执行,从而从任何地方赋予字典。有了这种自由度,就可以轻松地将设计转变为策略模式,其中每个委托都由执行该案例逻辑的唯一类提供。这支持代码封装和单一职责原则(一个类应该做一件事,并且应该是唯一负责该件事的类)。

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).
独木成林 2024-10-28 09:27:18

如果可能的情况较多,那么最好将 Switch 语句替换为 策略模式查看此内容

应用策略模式而不是使用 Switch 语句

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

国产ˉ祖宗 2024-10-28 09:27:18

目前还没有人对我认为这种方法的最大缺点发表任何言论。

它的可维护性较差。

我这么说有两个原因。

  1. 它的语法更加复杂。
  2. 需要更多的推理才能理解。

大多数程序员都知道 switch 语句是如何工作的。许多程序员从未见过函数字典。

虽然这看起来像是 switch 语句的一个有趣且新颖的替代方案,并且很可能是解决某些问题的唯一方法,但它要复杂得多。如果您不需要增加灵活性,则不应使用它。

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.

﹏雨一样淡蓝的深情 2024-10-28 09:27:18

将您的 A 类转换为分部类,并在另一个文件中创建第二个分部类,其中仅包含委托字典。

现在,您可以更改分支数量,并向 switch 语句添加逻辑,而无需触及类其余部分的源代码。

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.

云胡 2024-10-28 09:27:18

(无论语言如何)从性能角度来看,如果此类代码存在于关键部分,那么使用函数查找表几乎肯定会更好。

原因是您消除了多个运行时条件(切换时间越长,比较就越多),有利于简单的数组索引和函数调用。

唯一的性能缺点是您引入了函数调用的成本。这通常比所述条件更好。描述差异; YMMV。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文