c# - .Net switch 语句是散列的还是索引的?

标签 c# .net switch-statement

.Net 4(或任何先前版本)是否对基于字符串的较长 switch 语句执行任何类型的优化?

我正在解决一个潜在的性能瓶颈,因为一些长的 switch 语句在案例中寻找匹配的字符串,我一直假设这些是在线性时间内搜索的(或接近线性,即不使用索引来快速搜索找到匹配的字符串)。但这似乎是 .Net 可以优化的一个明显领域,所以我想我会检查是否是这种情况。

这是我最近的一个派生问题:indexed switch statement, or equivalent? .net, C#

最佳答案

编译以下代码。

public static int Main(string[] args)
{
    switch (args[0])
    {
        case "x": return 1;
        case "y": return 2;
        case "z": return 3;
    }
    return 0;
}

现在使用 ReflectorILDASM检查 C# 编译器生成的 IL。继续添加case语句,反编译,观察结果。

  • 如果 case 语句的数量很少,那么编译器会发出顺序相等比较。
  • 如果 case 语句的数量很大,那么编译器会发出一个 Dictionary 查找。

我使用的是 C# 3.0 编译器,我观察到策略在 7 个 case 语句处发生了变化。我怀疑您会看到与 C# 4.0 和其他版本类似的东西。

更新:

我应该指出,您会在 IL 输出中看到对 Dictionary.Add 的调用,它正在构建字典供以后使用。不要误以为这种情况每次都会发生。编译器实际上生成一个单独的静态类并对其进行内联静态初始化。请特别注意 L_0026 处的说明。如果该类已经初始化,则分支将跳过 Add 调用。

L_0021: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> <PrivateImplementationDetails>{816396DD-F271-4C12-83D0-CC9C9CD67AD6}::$$method0x6000001-1
L_0026: brtrue.s L_0089
L_0028: ldc.i4.7 
L_0029: newobj instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::.ctor(int32)
L_002e: dup 
L_002f: ldstr "x"
L_0034: ldc.i4.0 
L_0035: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
L_003a: dup 
L_003b: ldstr "y"
L_0040: ldc.i4.1 
L_0041: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
L_0046: dup 
L_0047: ldstr "z"
L_004c: ldc.i4.2 
L_004d: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)

另外,请注意字典实际上包含从原始字符串到整数的映射。该整数用于在 IL 中制定一个单独的开关。

L_0089: volatile. 
L_008b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> <PrivateImplementationDetails>{816396DD-F271-4C12-83D0-CC9C9CD67AD6}::$$method0x6000001-1
L_0090: ldloc.2 
L_0091: ldloca.s CS$0$0002
L_0093: call instance bool [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::TryGetValue(!0, !1&)
L_0098: brfalse.s L_00da
L_009a: ldloc.3 
L_009b: switch (L_00be, L_00c2, L_00c6, L_00ca, L_00ce, L_00d2, L_00d6)
L_00bc: br.s L_00da
L_00be: ldc.i4.1 
L_00bf: stloc.1 
L_00c0: br.s L_00de
L_00c2: ldc.i4.2 
L_00c3: stloc.1 
L_00c4: br.s L_00de
L_00c6: ldc.i4.3 

更新 2:

对于它的值(value),VB.NET 似乎没有对其 Select 构造进行同样的优化。

关于c# - .Net switch 语句是散列的还是索引的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3366376/

相关文章:

.net - 在 DataTrigger 条件下使用绑定(bind)

java - Switch 语句在 Java 中不适用于枚举

Java比较值,最好的方法

c# - 将点的 ArrayList 转换为字节数组以存储在 SQL 数据库中

c# - WPF 应用程序的异步和填充数据

c# - 我可以以编程方式将外部 exe 标记为 "safe to run (Unknown publisher)"吗?

Javascript - 从一种 switch 语句情况到另一种 switch 语句

c# Nhibernate Session 不更新

.net - 如何判断谁登录了 SQL Server

.net - .NET 中对象的自定义序列化