当前位置:  开发笔记 > 编程语言 > 正文

切换声明在C#中落后?

如何解决《切换声明在C#中落后?》经验,为你挑选了6个好方法。

切换声明是我喜爱switchif/else if构造的个人主要原因之一.这里有一个例子:

static string NumberToWords(int number)
{
    string[] numbers = new string[] 
        { "", "one", "two", "three", "four", "five", 
          "six", "seven", "eight", "nine" };
    string[] tens = new string[] 
        { "", "", "twenty", "thirty", "forty", "fifty", 
          "sixty", "seventy", "eighty", "ninety" };
    string[] teens = new string[]
        { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
          "sixteen", "seventeen", "eighteen", "nineteen" };

    string ans = "";
    switch (number.ToString().Length)
    {
        case 3:
            ans += string.Format("{0} hundred and ", numbers[number / 100]);
        case 2:
            int t = (number / 10) % 10;
            if (t == 1)
            {
                ans += teens[number % 10];
                break;
            }
            else if (t > 1)
                ans += string.Format("{0}-", tens[t]);
        case 1:
            int o = number % 10;
            ans += numbers[o];

            break;
        default:
            throw new ArgumentException("number");
    }
    return ans;
}

聪明的人正在畏缩,因为string[]s应该在函数之外声明:嗯,他们是,这只是一个例子.

编译器失败并出现以下错误:

Control cannot fall through from one case label ('case 3:') to another
Control cannot fall through from one case label ('case 2:') to another

为什么?有没有办法在没有三个ifs的情况下获得这种行为?



1> Alex Lyman..:

(复制/粘贴我在其他地方提供的答案)

通过下降switch- caseS可通过不具有代码在一个来实现case(见case 0),或者使用特殊的goto case(见case 1)或goto default(见case 2)形式:

switch (/*...*/) {
    case 0: // shares the exact same code as case 1
    case 1:
        // do something
        goto case 2;
    case 2:
        // do something else
        goto default;
    default:
        // do something entirely different
        break;
}


哇...我刚在代码中使用了`goto`.我应该感觉脏吗?
我认为,在这个特殊情况下,goto不被认为是有害的.
该死的 - 我从1.0开始就一直用C#进行编程,直到现在我都没见过.只是去展示,你每天都在学习新事物.
这一切都很好,Erik.唯一的原因/我/知道它是我是编译器理论书呆子谁用放大镜阅读ECMA-334规范.
@Dancrumb:在编写该功能时,C#尚未添加任何"软"关键字(如'yield','var','from'和'select'),因此它们有三个实际选项:1 )使'fallthrough'成为一个硬关键字(你不能将它用作变量名),2)编写支持这种软关键字所需的代码,3)使用已保留的关键字.对于那些移植代码来说,#1是个大问题; 根据我的理解,#2是一项相当大的工程任务; 和他们选择的选项一样,#3有一个附带的好处:其他开发人员在事实之后阅读代码可以从goto的基本概念中了解这个特性
所有这些都是关于新的/特殊的关键词,以便进行明确的考虑.难道他们只是使用了'continue'关键字吗?换一种说法.打开开关或继续下一个案例(通过).
@Jeff Dege - 如果你正在打开一个字符串,那么`case("XYZ")的`goto`语句:`应该写成`goto case("XYZ");`
创建一个新的保留词"fallthrough"是否有点过分,你可以用它来表明允许通过的明确愿望.然后,编译器可以检查意外跌落,但允许有目的的使用.
我以前从未想过在我上次使用它时,我会在80年代使用一些模糊的Turbo Pascal 3功能时使用'goto`语句.:)这实际上节省了我几分钟.
@ TalEven-Tov如果`switch`在循环中怎么办?
@Atømix:是的,可能.但是热水淋浴和一些重构无法解决这个问题:P
我恨,恨,恨这个.不是对问题或答案的反思,我只是讨厌这在C#中是必要的.
@ scott.korin为什么?你更喜欢隐含的堕落吗?我很确定过去30年的C语言使用情况告诉我们这个想法多么可怕.唯一的另一个真正的选择是甚至不支持这个,并强制代码重复,或(喘气)标准goto标签.

2> Jon Skeet..:

"为什么"是为了避免意外跌倒,对此我感激不尽.这是C和Java中不常见的错误来源.

解决方法是使用goto,例如

switch (number.ToString().Length)
{
    case 3:
        ans += string.Format("{0} hundred and ", numbers[number / 100]);
        goto case 2;
    case 2:
    // Etc
}

在我看来,开关/外壳的总体设计有点不幸.它离C太近 - 有一些有用的变化可以在范围等方面进行.可以说,一个更聪明的开关可以进行模式匹配等会有所帮助,但这确实从开关变为"检查一系列条件" - 此时可能会要求使用其他名称.


@ TalEven-Tov:编译器警告应该适用于几乎总能修复代码更好的情况.就个人而言,我更喜欢隐含的突破,所以开始时不会有问题,但这是另一回事.
如果是为了防止意外跌落,那么我觉得编译器警告本来会更好.就像你的if语句有一个赋值一样:`if(result = true){}`

3> Coincoin..:

交换机漏洞历史上是现代软件中错误的主要来源之一.语言设计者决定强制要求在案例结束时跳转,除非您在没有处理的情况下直接默认为下一个案例.

switch(value)
{
    case 1:// this is still legal
    case 2:
}


我永远不明白为什么那不是"案例1,2:"
@David Pfeffer:是的,所以`case 1,2,`:`允许这样的语言.我永远不会理解为什么任何现代语言都不会选择允许这样做.

4> Jon Hanna..:

为了在这里添加答案,我认为值得考虑与此相关的相反问题,即.为什么C首先允许掉线?

任何编程语言当然都有两个目标:

    向计算机提供说明.

    留下程序员的意图记录.

因此,任何编程语言的创建都是如何最好地服务于这两个目标之间的平衡.一方面,变得更容易变成计算机指令(无论是机器代码,字节码如IL,还是指令在执行时被解释),那么编译或解释过程将更加高效,可靠和紧凑的输出.尽管如此,这个目标导致我们只是在汇编,IL或甚至原始操作码中编写,因为最简单的编译是根本没有编译的地方.

相反,语言表达程序员的意图越多,而不是为此目的采取的手段,在编写和维护期间程序就越容易理解.

现在,switch总是可以通过将其转换为等效的if-else块链或类似链来编译,但它被设计为允许编译成特定的公共汇编模式,其中一个获取值,计算它的偏移量(无论是通过查找表通过值的完美散列索引,或通过值*的实际算术索引.值得注意的是,今天,C#编译有时会switch变成等效的if-else,有时会使用基于散列的跳转方法(同样使用C,C++和其他具有可比语法的语言).

在这种情况下,有两个很好的理由允许掉期:

    它无论如何都是自然发生的:如果你将一个跳转表构建成一组指令,并且其中一个早期批量指令不包含某种跳转或返回,那么执行将自然地进入下一批.如果你将switch-using C转换为使用机器码的跳转表,那么允许直通就是"刚刚发生" .

    在汇编中编写的编码器已经习惯了等价物:当在汇编中手动编写跳转表时,他们必须考虑给定的代码块是以返回结束,跳出表格还是继续到下一个街区.因此,让编码器break在必要时添加明确的内容对于编码器来说也是"自然的".

因此,在计算机语言的两个目标之间进行平衡是合理的尝试,因为它与生成的机器代码和源代码的表达性有关.

四十年后,情况并不完全相同,原因有以下几点:

    今天C中的编码员可能很少或根本没有装配经验.许多其他C风格语言的编码器甚至不太可能(尤其是Javascript!).任何"人们习惯于装配"的概念都不再相关.

    优化的改进意味着,由于被认为可能最有效的方法,或者转变为跳转表方法的特别深奥的变体,所以switch被转变的可能性if-else更高.较高级别和较低级别方法之间的映射不像以前那样强大.

    经验表明,跌倒往往是少数情况而不是常态(对Sun的编译器的研究发现,3%的switch块使用了同一块上的多个标签以外的跌倒,并且认为使用 -这里的案例意味着这3%实际上远高于正常水平).因此,所研究的语言使得不寻常的事情比普通的更容易照顾.

    经验表明,在意外完成的情况下,以及在维护代码的人错过正确的漏报的情况下,跌倒往往是问题的根源.后者是与掉落相关的错误的一个微妙的补充,因为即使您的代码完全没有错误,您的堕落仍然可能导致问题.

与最后两点相关,请考虑当前版本的K&R中的以下引用:

从一个案例落到另一个案例并不健全,在修改程序时容易崩溃.除了单个计算的多个标签之外,应谨慎使用漏洞并进行评论.

作为一个好的形式,在最后一个案例(默认在这里)之后休息,即使它在逻辑上是不必要的.有一天,当最后添加另一个案例时,这一点防御性编程将为您节省开支.

因此,从马的口中,C中的跌落是有问题的.总是记录带有注释的漏洞是一种良好的做法,这是应用一般原则的应用,应该记录一个人做一些不寻常的事情,因为这将是后来检查代码和/或使你的代码看起来像什么的当它实际上是正确的时,它有一个新手的错误.

当你考虑它时,代码如下:

switch(x)
{
  case 1:
   foo();
   /* FALLTHRU */
  case 2:
    bar();
    break;
}

添加的东西,使落空在代码中明确的,它只是没有东西可以被检测(或者其缺乏可检测)的编译器.

因此,事实上必须明确表示C#中的堕落并不会给那些在其他C风格的语言中写得很好的人增加任何惩罚,因为他们已经在他们的堕落中明确表示.†

最后,goto这里的使用已经是C和其他类似语言的标准:

switch(x)
{
  case 0:
  case 1:
  case 2:
    foo();
    goto below_six;
  case 3:
    bar();
    goto below_six;
  case 4:
    baz();
    /* FALLTHRU */
  case 5:
  below_six:
    qux();
    break;
  default:
    quux();
}

在这种情况下,我们希望将一个块包含在为除前一个块之外的值执行的代码中执行的代码中,那么我们就必须使用它goto.(当然,有一些方法和方法可以通过不同的条件避免这种情况,但对于与这个问题相关的所有事情都是如此).因此,C#建立在已经正常的方式来处理我们想要在a中击中多个代码块的一种情况switch,并且只是将其概括为覆盖掉落.它还使这两种情况更方便和自我记录,因为我们必须在C中添加一个新标签,但可以case在C#中使用它作为标签.在C#中,我们可以摆脱below_six标签和使用goto case 5,这更清楚我们正在做什么.(我们也不得不添加breakdefault,这是我离开了只是为了让上面的C代码显然不是C#代码).

因此总结如下:

    C#不再像40年前的C代码那样直接与未经优化的编译器输出相关(这些天也不是C),这使得跌倒的灵感之一无关紧要.

    C#与C语言保持兼容,不仅具有隐含性break,便于熟悉类似语言的人学习语言,并且更容易移植.

    C#删除了一个可能的错误来源或被误解的代码,这些代码在过去四十年中已被充分记录为导致问题.

    C#通过编译器强制执行C(文档落实)现有的最佳实践.

    C#使得不常见的情况是具有更明确代码的情况,通常情况下代码之一只是自动写入.

    C#使用相同goto的方法从C中使用的不同case标签中命中相同的块.它只是将其推广到其他一些情况.

    goto通过允许case语句充当标签,C#使得基于该方法的方法比在C中更方便,更清晰.

总而言之,这是一个非常合理的设计决策


*某些形式的BASIC可以让人们做出GOTO (x AND 7) * 50 + 240类似的,而脆弱的,因此一个特别有说服力的禁止的情况goto,确实有助于显示更高级的语言相当于低级代码可以跳过的方式算术一个值,当它是编译的结果而不是必须手动维护的东西时更合理.Duff设备的实现尤其适用于等效的机器代码或IL,因为每个指令块通常具有相同的长度而无需添加nop填充物.

†Duff的设备再次出现在这里,作为一个合理的例外.事实上,即使没有对这种效果的明确评论,使用这种和相似的模式重复操作也可以使得使用跌倒相对清晰.



5> kenny..:

你可以'转到案例标签' http://www.blackwasp.co.uk/CSharpGoto.aspx

goto语句是一个简单的命令,它无条件地将程序的控制权转移到另一个语句.该命令经常受到一些开发人员的批评,他们主张将其从所有高级编程语言中删除,因为它可能导致意大利面条代码.当有如此多的goto语句或类似的跳转语句使代码难以阅读和维护时,会发生这种情况.但是,有些程序员指出goto语句在仔细使用时可以为某些问题提供优雅的解决方案......



6> Biri..:

他们通过设计省略了这种行为,以避免它不被意志使用但引起问题.

只有在案例部分中没有语句时才能使用它,例如:

switch (whatever)
{
    case 1:
    case 2:
    case 3: boo; break;
}

推荐阅读
爱唱歌的郭少文_
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有