首页 > 解决方案 > 为什么编译器会在 switch 中添加语句?

问题描述

我有以下相当简单的 switch 语句。// 较早的字符串 fullPath = GetFullPath(); 字符串类型 = GetEntityType();

switch (type.ToLower()) {
    case "tables":
        tables.Add(fullPath);
        break;
    case "views":
        views.Add(fullPath);
        break;
    case "functions":
        functions.Add(fullPath);
        break;
    case "storedprocs":
        storedprocs.Add(fullPath);
        break;
    case "data":
        data.Add(fullPath);
        break;
    case "layouts":
        layouts.Add(fullPath);
        break;
    case "scripts":
        scripts.Add(fullPath);
        break;
    default:
        Console.WriteLine($"What is this: {type}");
        break;
}

当我使用 Reflector 反编译生成的二进制文件时,switch(string)它已更改为 ComputeStringHash,然后在每个 case 语句中,它通过if语句检查值。听起来它正在做双倍的工作。

    string s = str2.ToLower();
    switch (<PrivateImplementationDetails>.ComputeStringHash(s))
    {
        case 0x20890fc4:
            if (s == "tables")
            {
                break;
            }
            goto Label_0218;

        case 0x454a414e:
            if (s == "functions")
            {
                goto Label_01DE;
            }
            goto Label_0218;

        case 0x4facf6d1:
            if (s == "views")
            {
                goto Label_01D3;
            }
            goto Label_0218;

        case 0xcdfe2cb3:
            if (s == "storedprocs")
            {
                goto Label_01E9;
            }
            goto Label_0218;

        case 0xd872e2a5:
            if (s == "data")
            {
                goto Label_01F4;
            }
            goto Label_0218;

        case 0x9b4a129b:
            if (s == "scripts")
            {
                goto Label_020C;
            }
            goto Label_0218;

        case 0xba971064:
            if (s == "layouts")
            {
                goto Label_0200;
            }
            goto Label_0218;

        default:
            goto Label_0218;
    }

    first.Add(fullPath);
    continue;
  Label_01D3:
      second.Add(fullPath);
      continue;
  Label_01DE:
      list3.Add(fullPath);
      continue;
  Label_01E9:
      list4.Add(fullPath);
      continue;
  Label_01F4:
      list5.Add(fullPath);
      continue;
  Label_0200:
      list6.Add(fullPath);
      continue;
  Label_020C:
      list7.Add(fullPath);
      continue;
  Label_0218:
    Console.WriteLine("What is this: " + str2);
}

标签: c#compiler-optimization

解决方案


这是一个非常聪明的优化,它可以让几乎独立于语句块中的switch字符串数量的时间来完成它的工作。case

这种优化是基于观察到相同字符串的哈希码必须相同。编译器不是逐个检查字符串是否相等,而是计算一次目标字符串的哈希,并在 O(1) 中执行基于表的查找。这使编译器达到所需的情况,此时编译器需要检查字符串是否实际相等。

请注意,当多个查找字符串具有相同的哈希码时,会出现一些罕见的情况。在这种情况下,生成的case语句将包含多个if以在具有相同哈希码的字符串中做出决定。

总的来说,这种行为模仿了基于散列的字典的行为:散列码确定case(相当于散列桶),而里面的一系列ifs 确定是否存在匹配项。这会带来更好的性能,因为它可以让编译器跳过不必要的检查。


推荐阅读