c# - 为什么编译器会在 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);
}
解决方案
这是一个非常聪明的优化,它可以让几乎独立于语句块中的switch
字符串数量的时间来完成它的工作。case
这种优化是基于观察到相同字符串的哈希码必须相同。编译器不是逐个检查字符串是否相等,而是计算一次目标字符串的哈希,并在 O(1) 中执行基于表的查找。这使编译器达到所需的情况,此时编译器需要检查字符串是否实际相等。
请注意,当多个查找字符串具有相同的哈希码时,会出现一些罕见的情况。在这种情况下,生成的case
语句将包含多个if
以在具有相同哈希码的字符串中做出决定。
总的来说,这种行为模仿了基于散列的字典的行为:散列码确定case
(相当于散列桶),而里面的一系列if
s 确定是否存在匹配项。这会带来更好的性能,因为它可以让编译器跳过不必要的检查。
推荐阅读
- javascript - 以自定义方式显示输入字段
- java - 如何使用 Java 生成高质量的 WAVE 文件
- docker - Dockerfile:如何在容器路径中挂载主机目录?
- java - 3 个支票账户 2 个没有透支但不想要透支的 cust 的构造函数得到一个
- python - python - 如何使用pandas在python中创建数据框?如果是这样的话?
- android - android customView 大小无法正常工作
- node.js - 如何使用节点js检查mongodb中是否存在集合?
- java - 使用 src/main 和 src/test 运行测试时出现问题
- java - 我怎样才能从firebase数据库中只带一个数据?
- node.js - Rockset -- 找不到模块“./rest/index”的模块声明。../../rest/index.js 已隐式键入任何