문제

스위치 진술서의 충격은 사랑에 대한 나의 개인적인 주요 이유 중 하나입니다. switch vs. if/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

왜요? 그리고 세 가지없이 이런 종류의 행동을 얻는 방법이 있습니까? if에스?

도움이 되었습니까?

해결책

(복사/붙여 넣기 답변 나는 다른 곳에 제공했습니다)

떨어지는 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;
}

다른 팁

"왜"는 우발적 인 낙상을 피하는 것입니다. 이것은 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에 너무 가까이 붙어 있습니다 - 범위를 일치시키는 등의 유용한 변화가있을 수 있습니다. 아마도 패턴 일치 등을 수행 할 수있는 더 똑똑한 스위치는 도움이 될 것이지만, 실제로 스위치에서 "일련의 조건을 확인하는 것"으로 변경됩니다. - 그 시점에서 다른 이름이 호출 될 것입니다.

스위치 러프는 역사적으로 현대 소프트웨어의 주요 버그 원 중 하나입니다. 언어 디자이너는 처리없이 다음 케이스를 직접 불이행하지 않는 한, 사건이 끝날 때까지 점프해야합니다.

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

여기에 답을 더하기 위해, 나는 이것과 함께 반대의 질문을 고려할 가치가 있다고 생각합니다. C가 처음에 낙상을 허용 한 이유는 무엇입니까?

모든 프로그래밍 언어는 물론 두 가지 목표를 제공합니다.

  1. 컴퓨터에 지침을 제공하십시오.
  2. 프로그래머의 의도에 대한 기록을 남겨 두십시오.

따라서 모든 프로그래밍 언어를 만드는 것은이 두 가지 목표를 가장 잘 제공하는 방법 사이의 균형입니다. 한편으로, 컴퓨터 지침으로 바꾸는 것이 더 쉬워지면 (기계 코드, IL과 같은 바이트 코드이든, 지침이 실행시 해석되는지) 편집 또는 해석 프로세스가 효율적이고 신뢰할 수 있으며, 신뢰할 수 있으며, 출력이 작습니다. 이 목표는 가장 쉬운 편집이 컴파일이 전혀없는 곳이기 때문에 우리의 어셈블리, IL 또는 원시 OP 코드에서 우리의 글쓰기를 초래합니다.

반대로, 언어는 그 목적을 얻는 수단보다는 프로그래머의 의도를 더 많이 표현할 수 있으며, 글쓰기와 유지 보수 중에 프로그램은 더 이해할 수 있습니다.

지금, switch 동등한 체인으로 변환하여 항상 컴파일 될 수 있습니다. if-else 블록 또는 이와 유사하지만 값을 취하는 특정 공통 어셈블리 패턴으로 편집 할 수 있도록 설계되었으며, 값의 완벽한 해시 또는 실제 산술로 인덱스 된 테이블을 찾아서 오프셋을 계산합니다. 값*). 이 시점에서 오늘날 C# 컴파일은 때때로 회전 할 것입니다. switch 동등한 상태로 if-else, 그리고 때로는 해시 기반 점프 접근 방식을 사용합니다 (및 C, C ++ 및 비슷한 구문이있는 다른 언어와 마찬가지로).

이 경우 낙상을 허용하는 두 가지 이유가 있습니다.

  1. 어쨌든 자연스럽게 발생합니다. 점프 테이블을 일련의 지침으로 만들고 이전의 지침 배치 중 하나에 어떤 점프 또는 반환이 포함되지 않으면, 실행은 자연스럽게 다음 배치로 진행됩니다. 낙상을 허용하는 것은 당신이 switch-C를 점프 테이블-사용 기계 코드로 사용합니다.

  2. 어셈블리에 쓴 코더는 이미 동등한 점에 익숙했습니다. 어셈블리에서 점프 테이블을 작성할 때 주어진 코드 블록이 반환으로 끝나거나 테이블 외부 점프로 끝날지 여부를 고려해야합니다. 다음 블록에. 따라서 코더가 명시 적으로 추가하도록합니다 break 필요할 때 코더에게도 "자연"이었다.

따라서 당시 생산 된 기계 코드와 소스 코드의 표현성과 관련하여 컴퓨터 언어의 두 가지 목표를 균형을 맞추려는 합리적인 시도였습니다.

그러나 40 년 후, 몇 가지 이유로 상황이 거의 같지 않습니다.

  1. 오늘날 C의 코더는 조립 경험이 거의 없거나 전혀 없을 수 있습니다. 다른 많은 C 스타일 언어의 코더는 훨씬 적습니다 (특히 JavaScript!). "사람들이 어셈블리에서 사용하는 것"이라는 개념은 더 이상 관련이 없습니다.
  2. 최적화의 개선은 가능성을 의미합니다 switch 로 바뀌고 있습니다 if-else 접근 방식이 가장 효율적 일 가능성이 높거나 다른 점프 테이블 접근법의 난해한 변형으로 바뀌는 것이 더 높습니다. 상위 및 하위 수준의 접근 방식 사이의 매핑은 예전만큼 강하지 않습니다.
  3. 경험은 낙상이 표준보다는 소수의 사례 인 경향이 있음을 보여주었습니다 (Sun의 컴파일러에 대한 연구는 3%의 3%가 발견되었습니다. switch 블록은 동일한 블록의 여러 라벨 이외의 다른 라벨 이외의 낙상을 사용했으며, 여기서 사용 사례는이 3%가 실제로 정상보다 훨씬 높다는 것을 의미했습니다). 따라서 연구 된 언어는 공통보다 비정상적으로 더 쉽게 음식을 제공합니다.
  4. 경험에 따르면 실수로 수행되는 경우와 코드를 유지하는 누군가가 올바른 낙상을 놓치는 경우에도 낙상이 문제의 원인이되는 것으로 나타났습니다. 후자는 코드가 완벽하게 버그가 없더라도 낙상이 여전히 문제를 일으킬 수 있기 때문에 낙상과 관련된 버그에 미묘한 추가 기능입니다.

마지막 두 지점과 관련하여 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#은 이미 정상적인 방법을 구축하여 하나의 코드 블록을 switch, 그리고 방금 가을을 덮기 위해 일반화했습니다. 또한 C에 새 레이블을 추가해야하지만 사용할 수 있기 때문에 두 경우 모두 더 편리하고 자체 문서화를 만들었습니다. case C#의 레이블로. C#에서 우리는 그것을 제거 할 수 있습니다 below_six 라벨과 사용 goto case 5 우리가하는 일에 대해 더 명확합니다. (우리는 또한 추가해야합니다 breakdefault, 위의 C 코드를 C# 코드가 아닌 명확하게 만들기 위해 외식했습니다).

따라서 요약하면 :

  1. C#은 더 이상 C 코드가 40 년 전 (요즘 C도)처럼 직접 최적화되지 않은 컴파일러 출력과 관련이 없으며, 이는 낙상의 영감 중 하나가 관련이 없습니다.
  2. C#은 암시 적뿐만 아니라 C와 호환됩니다. break, 유사한 언어에 익숙하고 포팅이 쉬운 사람들이 언어를 쉽게 배우기 위해.
  3. C#은 지난 40 년 동안 문제를 일으키는 것으로 잘 문서화 된 버그 소스 또는 오해 코드를 제거합니다.
  4. C#은 컴파일러가 C (Document Fall Througe)를 사용하여 기존 모범 사례를 시행 할 수 있도록합니다.
  5. C#은 비정상적인 케이스를 더 명시적인 코드를 가진 코드로 만듭니다. 일반적인 경우 코드가있는 경우는 자동으로 쓸 수 있습니다.
  6. C#은 동일하게 사용합니다 goto-다른 블록을 다른 블록을 치기위한 기반 접근법 case C에서 사용되는 레이블은 다른 경우에 일반화합니다.
  7. C#을 만듭니다 goto-기반 접근법은 C보다 더 편리하고 명확합니다. case 라벨 역할을하는 진술.

대체로, 꽤 합리적인 디자인 결정


*일부 형태의 기본은 GOTO (x AND 7) * 50 + 240 부서지기 쉬우 며 따라서 금지에 대한 특히 설득력있는 사건 goto, 하위 수준 코드가 값에 대한 산술을 기반으로 점프 할 수있는 방법에 동등한 상위에 해당하는 역할을하는 역할을합니다. 수동으로. Duff의 장치의 구현은 특히 각 지침 블록이 추가 될 필요없이 종종 동일한 길이가되기 때문에 동등한 기계 코드 또는 IL에 잘 적합합니다. nop 필러.

† Duff의 장치는 합리적인 예외로 여기에 다시 나타납니다. 그 패턴과 유사한 패턴으로 운영이 반복되면 해당 효과에 대한 명시적인 의견이 없어도 낙상을 상대적으로 명확하게 사용하는 역할을합니다.

당신은 'goto case label'을 할 수 있습니다http://www.blackwasp.co.uk/csharpgoto.aspx

GOTO 진술은 무조건 프로그램의 제어를 다른 진술로 전송하는 간단한 명령입니다. 이 명령은 종종 일부 개발자가 모든 고급 프로그래밍 언어에서 제거를 옹호하는 것으로 비판을받습니다. 스파게티 코드. 이것은 너무 많은 goto 문 또는 유사한 점프 명령문이있을 때 발생하여 코드를 읽고 유지하기가 어려워집니다. 그러나 GOTO 진술은 신중하게 사용할 때 몇 가지 문제에 대한 우아한 솔루션을 제공한다고 지적하는 프로그래머가 있습니다.

그들은 의지에 의해 사용되지 않고 문제를 일으켰을 때를 피하기 위해 디자인 으로이 행동을 제외했습니다.

케이스 부분에 명령문이없는 경우에만 사용할 수 있습니다.

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

C#에 대한 스위치 명령문 (C/Java/C ++) 동작을 변경했습니다. 추론은 사람들이 추락을 잊어 버렸고 오류가 발생했다고 생각합니다. 내가 읽은 한 권의 책은 Goto를 사용하여 시뮬레이션한다고 말했지만 이것은 나에게 좋은 해결책처럼 들리지 않습니다.

사례 문의 마지막 블록 또는 기본 문의 명령문을 포함하여 각 사례 블록 후에 브레이크와 같은 점프 명령문이 필요합니다. 한 가지 예외를 제외하고 (C ++ 스위치 명령문과 달리) C#은 한 케이스 레이블에서 다른 케이스 레이블로 암시 적 낙상을 지원하지 않습니다. 한 가지 예외는 case 문에 코드가없는 경우입니다.

-- C# switch () 문서

각 사례 명세서가 필요합니다 부서지다 또는 이동 기본 사례 인 경우에도 명령문.

GOTO 키워드로 C ++와 같은 가을을 달성 할 수 있습니다.

전:

switch(num)
{
   case 1:
      goto case 3;
   case 2:
      goto case 3;
   case 3:
      //do something
      break;
   case 4:
      //do something else
      break;
   case default:
      break;
}

Xamarin의 컴파일러가 실제로 잘못되었고이를 허용한다고 덧붙이는 간단한 메모 만 있습니다. 아마도 고정되었지만 공개되지 않았습니다. 실제로이를 실제로 겪고있는 일부 코드에서 발견했으며 컴파일러는 불평하지 않았습니다.

C#은 Switch/Case 문으로 Fall을 지원하지 않습니다. 이유는 확실하지 않지만 실제로는 지원이 없습니다. 결합

스위치 (C# 참조)가 말합니다

C#은 마지막 스위치 섹션을 포함하여 스위치 섹션의 끝이 필요합니다.

따라서 추가해야합니다 break; 너의 ~에게 default 섹션, 그렇지 않으면 컴파일러 오류가 여전히 있습니다.

"break"를 추가하는 것을 잊었습니다. 사례 3에 대한 설명. 사례 2에서는 IF 블록에 썼습니다. 그러므로 이것을 시도하십시오 :

case 3:            
{
    ans += string.Format("{0} hundred and ", numbers[number / 100]);
    break;
}


case 2:            
{
    int t = (number / 10) % 10;            
    if (t == 1)            
    {                
        ans += teens[number % 10];                
    }            
    else if (t > 1)                
    {
        ans += string.Format("{0}-", tens[t]);        
    }
    break;
}

case 1:            
{
    int o = number % 10;            
    ans += numbers[o];            
    break;        
}

default:            
{
    throw new ArgumentException("number");
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top