猿问

C#switch语句限制 - 为什么?

C#switch语句限制 - 为什么?

在编写switch语句时,在case语句中可以打开的内容似乎存在两个限制。

例如(是的,我知道,如果你正在做这种事情,这可能意味着你的面向对象(OO)架构是不确定的 - 这只是一个人为的例子!),

  Type t = typeof(int);

  switch (t) {

    case typeof(int):
      Console.WriteLine("int!");
      break;

    case typeof(string):
      Console.WriteLine("string!");
      break;

    default:
      Console.WriteLine("unknown!");
      break;
  }

这里switch()语句失败,带有'一个预期的整数类型的值',case语句失败并带有'a expected value is expected'。

为什么会有这些限制,以及基本理由是什么?我看不出有任何理由switch语句具有只能屈从于静态分析,为什么在接通的值必须是完整的(即原语)。理由是什么?


富国沪深
浏览 734回答 3
3回答

冉冉说

这是我的原帖,引发了一些争论...... 因为它错了:switch语句与if-else语句不同。每个案例必须是唯一的并且静态评估。无论您有多少个案例,switch语句都会执行一个恒定时间分支。if-else语句计算每个条件,直到找到一个为真。实际上,C#switch语句并不总是一个恒定时间分支。在某些情况下,编译器将使用CIL开关语句,该语句确实是使用跳转表的恒定时间分支。然而,在Ivan Hamilton指出的稀疏情况下,编译器可能完全生成其他东西。这实际上很容易通过编写各种C#switch语句来验证,一些稀疏,一些密集,并使用ildasm.exe工具查看生成的CIL。

慕桂英4014372

重要的是不要将C#switch语句与CIL开关指令混淆。CIL开关是一个跳转表,需要索引到一组跳转地址。这仅在C#开关的情况相邻时才有用:case&nbsp;3:&nbsp;blah;&nbsp;break;case&nbsp;4:&nbsp;blah;&nbsp;break;case&nbsp;5:&nbsp;blah;&nbsp;break;但如果不是这样的话,几乎没用:case&nbsp;10:&nbsp;blah;&nbsp;break;case&nbsp;200:&nbsp;blah;&nbsp;break;case&nbsp;3000:&nbsp;blah;&nbsp;break;(你需要一个表~3000个条目,只使用3个插槽)对于非相邻表达式,编译器可能会开始执行线性if-else-if-else检查。对于较大的非相邻表达式集,编译器可以从二叉树搜索开始,最后是if-else-if-else最后几个项。对于包含相邻项块的表达式集,编译器可以进行二叉树搜索,最后是CIL开关。这充满了“mays”和“mights”,它依赖于编译器(可能与Mono或Rotor不同)。我使用相邻的案例在我的机器上复制了你的结果:执行10路开关的总时间,10000次迭代(ms):每10路开关25.1383&nbsp;近似时间(ms):0.00251383执行50路开关的总时间,10000次迭代(ms):每50路开关的大约时间为26.593&nbsp;(ms):0.0026593执行5000路开关的总时间,10000次迭代(ms):23.7094&nbsp;每5000路开关的近似时间(ms):0.00237094执行50000路开关的总时间,10000次迭代(ms):20.0933&nbsp;每50000路开关的近似时间(ms):0.00200933然后我也使用了非相邻的case表达式:执行10路开关的总时间,10000次迭代(ms):19.6189&nbsp;每10路开关的近似时间(ms):0.00196189执行500路开关的总时间,10000次迭代(ms):每个500路开关的近似时间为19.1664&nbsp;(ms):0.00191664执行5000路开关的总时间,10000次迭代(ms):每5000路开关的大约时间为19.5871&nbsp;(ms):0.00195871不相邻的50,000个案例切换语句将无法编译。“在'ConsoleApplication1.Program.Main(string [])'附近编译表达式太长或太复杂这里有趣的是,二叉树搜索比CIL切换指令更快(可能不是统计上)。Brian,你使用了“&nbsp;常量&nbsp;”&nbsp;这个词,从计算复杂性理论的角度来看,它具有非常明确的含义。虽然简化的相邻整数示例可以产生被认为是O(1)(常数)的CIL,但稀疏示例是O(log n)(对数),聚类示例介于两者之间,小例子是O(n)(线性) )。这甚至不能解决Generic.Dictionary<string,int32>可能会创建静态的String情况,并且在首次使用时会遇到明确的开销。这里的表现将取决于表现Generic.Dictionary。如果检查C#语言规范(不是CIL规范),你会发现“15.7.2 switch语句”没有提到“常量时间”或底层实现甚至使用CIL开关指令(要非常小心假设这样的事情)。在一天结束时,针对现代系统上的整数表达式的C#切换是亚微秒操作,通常不值得担心。当然,这些时间将取决于机器和条件。我不会注意这些时序测试,我们所讨论的微秒持续时间与正在运行的任何“真实”代码相比相形见绌(并且您必须包含一些“真实代码”,否则编译器将优化分支),或者系统中的抖动。我的答案基于使用IL DASM来检查C#编译器创建的CIL。当然,这不是最终的,因为CPU运行的实际指令随后由JIT创建。我检查了在我的x86机器上实际执行的最终CPU指令,并且可以确认一个简单的相邻设置开关,例如:&nbsp;&nbsp;jmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds:300025F0[eax*4]二叉树搜索满满的地方:&nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebx,&nbsp;79Eh &nbsp;&nbsp;jg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3000352B &nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebx,&nbsp;654h &nbsp;&nbsp;jg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;300032BB &nbsp;&nbsp;… &nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebx,&nbsp;0F82h &nbsp;&nbsp;jz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;30005EEE
随时随地看视频慕课网APP
我要回答