c# - 为什么编译器要在开关中添加语句?

标签 c# compiler-optimization

我有以下相当简单的 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);
}

最佳答案

这是一个非常聪明的优化,它让 switch 及时完成它的工作,几乎与语句的 case block 中的字符串数量无关。

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

请注意,在极少数情况下,多个查找字符串会具有相同的哈希码。在这种情况下,生成的 case 语句将包含多个 if 来决定具有相同哈希码的字符串。

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

关于c# - 为什么编译器要在开关中添加语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53839624/

相关文章:

c# - 从内存中的视频截图,无需将视频或屏幕截图写入磁盘

c# - 表达树生成的IL是否经过优化?

c# - 编写将为每个列表元素添加值的 LINQ 语句

c# - 迁移 .NET 核心 2.0 缺少 IActionSelectorDecisionTreeProvider

java - 在同一 block 中调用方法两次。为什么?

java - Java 编译器是否优化了不必要的三元运算符?

c++ - 这个 C++ 编译器优化的名称是什么(在其自身实例上调用对象自己的构造函数),它是如何工作的?

c++ - 编译器会自动优化对数学函数的重复调用吗?

c# - 在 WPF 中使用交互触发器

c# - 我如何尝试创建一个文件,并返回一个值来指示它是否已创建?