`goto '부두를 피하는가?


47

switch처리해야 할 몇 가지 사례 가있는 구조가 있습니다. 는 switch과도하게 작동 enum값 조합을 통해 중복 된 코드의 문제를 제기한다 :

// All possible combinations of One - Eight.
public enum ExampleEnum {
    One,
    Two, TwoOne,
    Three, ThreeOne, ThreeTwo, ThreeOneTwo,
    Four, FourOne, FourTwo, FourThree, FourOneTwo, FourOneThree,
          FourTwoThree, FourOneTwoThree
    // ETC.
}

현재 switch구조는 각 값을 개별적으로 처리합니다.

// All possible combinations of One - Eight.
switch (enumValue) {
    case One: DrawOne; break;
    case Two: DrawTwo; break;
    case TwoOne:
        DrawOne;
        DrawTwo;
        break;
     case Three: DrawThree; break;
     ...
}

거기에서 아이디어를 얻습니다. 나는 현재 이것을 if한 줄로 조합을 처리하기 위해 스택 구조 로 세분화했습니다 .

// All possible combinations of One - Eight.
if (One || TwoOne || ThreeOne || ThreeOneTwo)
    DrawOne;
if (Two || TwoOne || ThreeTwo || ThreeOneTwo)
    DrawTwo;
if (Three || ThreeOne || ThreeTwo || ThreeOneTwo)
    DrawThree;

이것은 읽기에 혼란스럽고 유지하기 어려운 논리적 인 평가의 문제를 제기합니다. 이것을 리팩토링 한 후에 나는 대안들에 대해 생각하기 시작했고 switch사례들 사이에 틀에 박힌 구조 에 대한 아이디어를 생각하기 시작했다 .

폴 스루를 허용하지 않으므로이 goto경우에는를 사용해야합니다 C#. 그러나 switch구조 에서 뛰어 넘더라도 엄청나게 긴 논리 체인을 방지하고 여전히 코드 복제를 가져옵니다.

switch (enumVal) {
    case ThreeOneTwo: DrawThree; goto case TwoOne;
    case ThreeTwo: DrawThree; goto case Two;
    case ThreeOne: DrawThree; goto default;
    case TwoOne: DrawTwo; goto default;
    case Two: DrawTwo; break;
    default: DrawOne; break;
}

이것은 여전히 ​​충분히 깨끗하지 않은 솔루션이며 goto피하고 싶은 키워드 와 관련된 낙인이 있습니다 . 이것을 정리하는 더 좋은 방법이 있어야합니다.


내 질문

가독성과 유지 관리성에 영향을 미치지 않으면 서이 특정 사례를 처리하는 더 좋은 방법이 있습니까?


28
큰 토론을하고 싶은 것 같습니다. 그러나 goto를 사용하려면 좋은 사례에 대한 더 나은 예가 필요합니다. if 서술문 예제가 받아 들여지지 않고 나에게 잘
— Ewan

5
@Ewan 아무도 goto를 사용하지 않습니다. Linux 커널 소스 코드를 마지막으로 본 시점은 언제입니까?
— John Douma

6
귀하 goto의 언어로 고급 구조가 존재하지 않을 때 사용 합니다. 때로는 가끔 있었으면 좋겠다 fallthru. 키워드를 사용하면 특정 용도를 제거 할 수 goto있습니다.
— 여호수아

25
일반적으로 gotoC #과 같은 고급 언어 를 사용해야 할 필요가 있다고 생각되면 다른 많은 (더 나은) 디자인 및 / 또는 구현 대안을 간과했을 것입니다. 낙담했습니다.
— code_dredd

11
C #에서 "goto case"를 사용하는 것은보다 일반적인 "goto"를 사용하는 것과 다릅니다. 이는 명시적인 오류를 달성하는 방법이기 때문입니다.
— Hammerite

답변:


176

goto문장으로 코드를 읽기가 어렵다는 것을 알았습니다 . 나는 당신을 enum다르게 구성하는 것이 좋습니다 . 예를 들어, enum각 비트가 선택 사항 중 하나를 나타내는 비트 필드 인 경우 다음과 같이 보일 수 있습니다.

[Flags]
public enum ExampleEnum {
    One = 0b0001,
    Two = 0b0010,
    Three = 0b0100
};

Flags 속성은 컴파일러에게 겹치지 않는 값을 설정하고 있음을 알려줍니다. 이 코드를 호출하는 코드는 적절한 비트를 설정할 수 있습니다. 그런 다음 무슨 일이 일어나고 있는지 명확하게하기 위해 이와 같은 작업을 수행 할 수 있습니다.

if (myEnum.HasFlag(ExampleEnum.One))
{
    CallOne();
}
if (myEnum.HasFlag(ExampleEnum.Two))
{
    CallTwo();
}
if (myEnum.HasFlag(ExampleEnum.Three))
{
    CallThree();
}

myEnum비트 필드를 올바르게 설정하고 Flags 속성으로 표시된 코드가 필요 합니다. 그러나 예제의 열거 형 값을 다음과 같이 변경하여이를 수행 할 수 있습니다.

[Flags]
public enum ExampleEnum {
    One = 0b0001,
    Two = 0b0010,
    Three = 0b0100,
    OneAndTwo = One | Two,
    OneAndThree = One | Three,
    TwoAndThree = Two | Three
};

형식으로 숫자를 쓰면 0bxxxx이진수로 지정됩니다. 그래서 우리는 비트 1, 2 또는 3을 설정하는 것을 볼 수 있습니다 (기술적으로 0, 1 또는 2이지만 아이디어를 얻습니다). 조합이 자주 설정되는 경우 비트 단위 OR을 사용하여 조합의 이름을 지정할 수도 있습니다.


5
그것은 그렇게 나타납니다 ! 그러나 C # 7.0 이상이어야합니다.
— user1118321

31
어쨌든 다음과 같이 할 수도 있습니다 public enum ExampleEnum { One = 1 << 0, Two = 1 << 1, Three = 1 << 2, OneAndTwo = One | Two, OneAndThree = One | Three, TwoAndThree = Two | Three };. C # 7 +를 고집 할 필요가 없습니다.
— 중복 제거기


13
이 [Flags]속성은 컴파일러에 아무 것도 신호하지 않습니다. 이것이 열거 형 값을 2의 거듭 제곱으로 명시 적으로 선언해야하는 이유입니다.
— Joe Sewell

19
@UKMonkey 나는 크게 동의하지 않는다. HasFlag는 수행되는 작업에 대한 설명적이고 명시적인 용어이며 기능에서 구현을 추상화합니다. &다른 언어에서 일반적으로 사용 되므로 byte를 선언하는 대신 형식을 사용하는 것보다 더 의미가 없습니다 enum.
— BJ Myers

137

문제의 근본 원인은이 코드가 존재하지 않아야한다는 것입니다.

당신은 분명히 세 개의 독립적 인 조건을 가지고 있으며, 그러한 조건이 참이라면 취할 세 가지 독립적 인 조치를 취합니다. 그렇다면 왜 모든 것이 하나의 코드 조각으로 퍼져서 무엇을 해야하는지 알려주기 위해 세 개의 부울 플래그가 필요하고 (열거에 열거하지 않든 상관없이) 세 개의 독립적 인 것들을 결합합니까? 단일 책임 원칙은 여기서 하루를 보낸 것 같습니다.

소속 된 (즉, 조치를 수행해야 할 필요성을 발견 한) 세 가지 함수를 호출하고이 예제의 코드를 휴지통에 위탁하십시오.

3 개가 아닌 10 개의 플래그와 동작이있는 경우 1024 가지의 다른 조합을 처리하도록이 코드를 확장 하시겠습니까? 내가하지 희망! 1024가 너무 많으면 같은 이유로 8도 너무 많습니다.


10
사실, 모든 종류의 플래그, 옵션 및 기타 여러 매개 변수를 가진 잘 설계된 API가 많이 있습니다. 일부는 전달 될 수도 있고, 일부는 직접적인 영향을 미치지 만, SRP를 손상시키지 않으면 서 잠재적으로 무시 될 수도 있습니다. 어쨌든 함수는 외부 입력을 해독 할 수도 있습니다. 또한, reductio ad absurdum은 종종 시작점이 그다지 견고하지 않은 경우 종종 터무니없는 결과를 초래합니다.
— 중복 제거기

9
이 답변을 찬성했습니다. GOTO에 대해 토론하고 싶다면 그 질문과는 다른 질문입니다. 제시된 증거를 바탕으로 세 가지 분리 된 요구 사항이 있으며,이를 집계해서는 안됩니다. SE 관점에서, 나는 alephzero가 정답이라고 제출합니다.
— Haakon Dahl

8
@Deduplicator 1,2 & 3의 모든 조합이 아닌 일부의 이러한 혼란을 살펴보면 이것이 "잘 설계된 API"가 아님을 알 수 있습니다.
— user949300

4
너무 많은. 다른 답변은 질문의 내용을 실제로 개선하지 않습니다. 이 코드는 다시 작성해야합니다. 더 많은 함수를 작성하는 데 아무런 문제가 없습니다.
— only_pro

3
이 답변을 좋아합니다. 특히 "1024가 너무 많으면 8도 너무 많습니다". 그러나 그것은 본질적으로 "다형성 사용"입니까? 그리고 내 (약한) 대답은 그 말에 대해 많은 쓰레기를 얻고 있습니다. 홍보 담당자를 고용 할 수 있습니까? :-)
— user949300

29

gotos를 사용하지 마십시오 는 컴퓨터 과학의 "어린이에게 거짓말" 개념 중 하나입니다 . 시간의 99 %가 올바른 조언이며, 너무 드물고 특별한 시간이 아니므로 새로운 코더에게 "사용하지 마십시오"라고 설명하면 모든 사람에게 훨씬 좋습니다.

그래서 언제 사용해야 합니까? 있다 몇 가지 시나리오가 있지만 타격 것 같다 기본적인 일이다 : 당신이 상태 머신 (state machine)을 코딩 할 때 . 상태 머신보다 더 체계적이고 체계적인 알고리즘 표현이 없다면 코드의 자연 표현에는 구조화되지 않은 브랜치가 포함되며, 논란의 여지가없는 구조에 대해서는 할 수있는 일이 많지 않습니다. 상태 머신 자체 보다 오히려 이하, 무명.

컴파일러 작가는 LALR 파서를 구현하는 대부분의 컴파일러의 소스 코드가 이유입니다, 이것을 알고 * gotos이 포함되어 있습니다. 그러나 실제로 자신의 어휘 분석기와 파서를 코딩하는 사람은 거의 없습니다.

* IIRC, 테이블 또는 기타 구조화되지 않은 제어문을 사용하지 않고 LALL 문법을 완전히 재귀 강등 적으로 구현할 수 있으므로 진정으로 반 고토 (anti-goto) 인 경우이 방법을 사용할 수 있습니다.


다음 질문은 "이 예제는 그 중 하나입니까?"입니다.

내가보고있는 것은 현재 상태의 처리에 따라 세 가지 가능한 다음 상태가 있다는 것입니다. 그중 하나 ( "default")는 코드의 한 줄에 불과하기 때문에 기술적으로 해당 코드의 끝 부분에 해당 코드를 적용하여 제거 할 수 있습니다. 그러면 다음 두 가지 상태로 넘어갈 수 있습니다.

남은 것 중 하나 ( "Three")는 내가 볼 수있는 한 곳에서만 나옵니다. 따라서 같은 방식으로 제거 할 수 있습니다. 다음과 같은 코드가 생깁니다.

switch (exampleValue) {
    case OneAndTwo: i += 3 break;
    case OneAndThree: i += 4 break;
    case Two: i += 2 break;
    case TwoAndThree: i += 5 break;
    case Three: i += 3 break;
    default: i++ break;
}

그러나 이것은 다시 당신이 제공 한 장난감 사례였습니다. "기본"그것은 코드의 적지 않은 양이 경우에서는 "세"가 여러 국가에서로 전환되거나 (가장 중요) 더 유지 관리 -이 상태를 추가하거나 복잡 가능성 , 솔직히 더 나을 것 gotos 사용 (그리고 아마도 머무를 필요가없는 좋은 이유가 없다면 사물의 상태 기계 특성을 숨기는 열거 형 구조를 제거 할 수도 있습니다).


2
@PerpetualJ-글쎄, 당신이 더 깨끗한 것을 발견하게되어 기쁩니다. 그것이 실제로 당신의 상황이라면, 고 토스 (케이스 또는 열거 형이 아니라 단지 블록과 고 토스로 레이블이 지정됨)로 시도하고 그것들이 가독성으로 어떻게 비교되는지 보는 것이 여전히 흥미로울 수 있습니다. 즉, "goto"옵션은 더 읽기 쉬울뿐만 아니라 다른 개발자의 인수와 "수정 된"수정 시도를 차단하기에 충분히 읽기 쉬울 수 있으므로 최상의 옵션을 찾은 것 같습니다.
— TED

4
"추가 유지 보수가 상태를 추가하거나 복잡하게 할 가능성이있는 경우"gotos를 사용하는 것이 더 나을 것 "이라고 생각하지 않습니다. 스파게티 코드가 구조화 된 코드보다 유지 관리가 더 쉽다고 주장하고 있습니까? 이것은 ~ 40 년의 실습에 위배됩니다.
— user949300

5
@ user949300-상태 머신 구축에 대해 연습하지는 않습니다. 실제 예를 들어이 답변에 대한 이전 의견을 간단히 읽을 수 있습니다. 본질적으로 그러한 제한이없는 상태 머신 알고리즘에 인공 구조 (그들의 선형 순서)를 부과하는 C 케이스 폴 스루를 사용하려고하면 모든 것을 재정렬해야 할 때마다 기하학적으로 복잡성이 증가합니다. 새로운 상태를 수용하는 주문. 알고리즘 호출과 같은 상태와 전환 만 코딩하면 발생하지 않습니다. 새로운 상태는 추가하기가 쉽지 않습니다.
— TED

8
@ user949300- "~ 40 년의 실습"컴파일러 작성은 고토가 실제로 문제 영역의 일부에 가장 적합한 방법임을 보여줍니다. 따라서 컴파일러 소스 코드를 보면 거의 항상 고 토스를 찾을 수 있습니다.
— TED

3
@ user949300 스파게티 코드는 특정 기능이나 규칙을 사용할 때 본질적으로 나타나지 않습니다. 언제든지 모든 언어, 기능 또는 규칙으로 나타날 수 있습니다. 그 원인은 의도하지 않은 응용 프로그램에서 해당 언어, 기능 또는 규칙을 사용하고 그것을 뒤로 당겨 구부러지게 만드는 것입니다. 항상 작업에 적합한 도구를 사용하십시오 goto. 때로는을 사용하는 것이 좋습니다.
— Abion47

27

가장 좋은 대답은 다형성을 사용하는 것입니다 .

또 다른 대답, IMO는 if 물건을보다 명확 하고 논쟁의 여지없이 짧게 만듭니다 .

if (One || OneAndTwo || OneAndThree)
  CallOne();
if (Two || OneAndTwo || TwoAndThree)
  CallTwo();
if (Three || OneAndThree || TwoAndThree)
  CallThree();

goto 아마도 내 58 번째 선택 일 것입니다 ...


5
이상적으로는 클라이언트 코드를 단순히 "Call ();"로 단순화 할 수있는 다형성 제안 +1, tell do n't ask를 사용하여 의사 결정 논리를 소비 클라이언트에서 클래스 메커니즘 및 계층 구조로 푸시하십시오.
— Erik Eidt

12
보이지 않는 최고의 광경을 선포하는 것은 매우 용감합니다. 그러나 추가 정보가 없으면 약간의 회의론과 열린 마음을 유지하는 것이 좋습니다. 아마도 조합 폭발로 고통 받고 있습니까?
— 중복 제거기

와우, 나는 이것이 다형성을 제안한 것에 대해 처음으로 downvoted 된 것이라고 생각합니다. :-)
— user949300

25
어떤 사람들은 문제에 직면했을 때 "다형성을 사용하겠습니다"라고 말합니다. 이제 두 가지 문제와 다형성이 있습니다.
— Monica와 Lightness Races

8
실제로 컴파일러의 상태 머신에서 고 토스를 없애기 위해 런타임 다형성을 사용하는 것에 대한 석사 논문을 작성했습니다. 이것은 가능하지만 실제로 실제로 대부분의 경우 노력할 가치가 없다는 것을 알았습니다. 많은 OO 설정 코드가 필요하며, 모두 버그로 끝날 수 있습니다 (이 답변은 생략됩니다).
— TED

10

왜 그렇지 않습니까?

public enum ExampleEnum {
    One = 0, // Why not?
    OneAndTwo,
    OneAndThree,
    Two,
    TwoAndThree,
    Three
}
int[] COUNTS = { 1, 3, 4, 2, 5, 3 }; // Whatever

int ComputeExampleValue(int i, ExampleEnum exampleValue) {
    return i + COUNTS[(int)exampleValue];
}

좋아, 나는 이것이 해킹 적이라고 생각한다 (나는 C # 개발자 btw가 아니므로 코드에 대해 실례한다.)하지만 효율성의 관점에서 이것은 반드시해야합니까? 배열 인덱스로 열거 형을 사용하는 것은 유효한 C #입니다.


3
예. 코드를 데이터로 대체하는 또 다른 예 (일반적으로 속도, 구조 및 유지 관리가 바람직 함).
— 베드로-모니카 복원 복원

2
그것은 의심의 여지없이 가장 최근의 질문에 대한 훌륭한 아이디어입니다. 그리고 첫 번째 열거 형 (밀한 값 범위)에서 일반적으로 수행되어야하는 다소 독립적 인 조치의 플래그로 이동하는 데 사용할 수 있습니다.
— 중복 제거기

C # 컴파일러에 액세스 할 수 없지만 다음과 같은 코드를 사용하여 코드를 더 안전하게 만들 수 있습니다 int[ExempleEnum.length] COUNTS = { 1, 3, 4, 2, 5, 3 };.
— Laurent Grégoire

7

플래그를 사용할 수 없거나 사용하지 않으려면 꼬리 재귀 함수를 사용하십시오. 64 비트 릴리스 모드에서 컴파일러는 goto명령문 과 매우 유사한 코드를 생성 합니다. 당신은 그것을 처리 할 필요가 없습니다.

int ComputeExampleValue(int i, ExampleEnum exampleValue) {
    switch (exampleValue) {
        case One: return i + 1;
        case OneAndTwo: return ComputeExampleValue(i + 2, ExampleEnum.One);
        case OneAndThree: return ComputeExampleValue(i + 3, ExampleEnum.One);
        case Two: return i + 2;
        case TwoAndThree: return ComputeExampleValue(i + 2, ExampleEnum.Three);
        case Three: return i + 3;
   }
}

그것은 독특한 해결책입니다! +1 나는 이런 식으로해야한다고 생각하지 않지만, 미래의 독자들에게는 훌륭합니다!
— PerpetualJ

2

허용되는 솔루션은 문제가되지 않으며 문제에 대한 구체적인 솔루션입니다. 그러나 더 추상적 인 대안을 찾고 싶습니다.

내 경험상 논리 흐름을 정의하기 위해 열거 형을 사용하면 코드 디자인이 좋지 않은 경우가 종종 있습니다.

작년에 작업 한 코드에서 이런 일이 발생하는 실제 사례에 부딪 쳤습니다. 원래 개발자는 가져 오기 및 내보내기 논리를 모두 수행하고 열거 형을 기반으로 두 클래스 사이를 전환하는 단일 클래스를 만들었습니다. 이제 코드는 비슷하고 일부 중복 코드가 있었지만 코드를 읽기 어렵고 테스트하기가 거의 불가능 해졌습니다. 나는 그것을 두 개의 개별 클래스로 리팩토링하여 결국 모두 단순화하고 실제로보고되지 않은 많은 버그를 발견하고 제거 할 수있었습니다.

다시 한 번, 열거 형을 사용하여 논리 흐름을 제어하는 ​​것은 종종 디자인 문제라고 언급해야합니다. 일반적으로 Enum은 가능한 값이 명확하게 정의 된 형식에 안전하고 소비자에게 친숙한 값을 제공하는 데 주로 사용해야합니다. 논리 제어 메커니즘보다 속성 (예 : 테이블의 열 ID)으로 더 잘 사용됩니다.

질문에 제시된 문제를 고려해 봅시다. 나는 여기의 맥락이 나이 열거가 무엇을 나타내는 지 정말로 모른다. 그림 카드인가요? 그림을 그리기? 피를 뽑아? 순서가 중요합니까? 또한 성능이 얼마나 중요한지 모르겠습니다. 성능이나 메모리가 중요한 경우이 솔루션은 원하는 솔루션이 아닐 수 있습니다.

어쨌든 열거 형을 고려하십시오.

// All possible combinations of One - Eight.
public enum ExampleEnum {
    One,
    Two,
    TwoOne,
    Three,
    ThreeOne,
    ThreeTwo,
    ThreeOneTwo
}

우리가 여기에 가지고있는 것은 다른 비즈니스 개념을 나타내는 많은 다른 열거 형 값입니다.

대신 사용할 수있는 것은 사물을 단순화하는 추상화입니다.

다음 인터페이스를 고려해 봅시다.

public interface IExample
{
  void Draw();
}

그런 다음 이것을 추상 클래스로 구현할 수 있습니다.

public abstract class ExampleClassBase : IExample
{
  public abstract void Draw();
  // other common functionality defined here
}

우리는 그림 1, 2, 3 (논쟁을 위해 다른 논리를 가짐)을 나타내는 구체적인 클래스를 가질 수 있습니다. 이것들은 위에서 정의한 기본 클래스를 잠재적으로 사용할 수 있지만 DrawOne 개념이 열거 형으로 표현 된 개념과 다르다고 가정합니다.

public class DrawOne
{
  public void Draw()
  {
    // Drawing logic here
  }
}

public class DrawTwo
{
  public void Draw()
  {
    // Drawing two logic here
  }
}

public class DrawThree
{
  public void Draw()
  {
    // Drawing three logic here
  }
}

그리고 이제 우리는 다른 클래스에 대한 논리를 제공하기 위해 구성 될 수있는 세 개의 개별 클래스가 있습니다.

public class One : ExampleClassBase
{
  private DrawOne drawOne;

  public One(DrawOne drawOne)
  {
    this.drawOne = drawOne;
  }

  public void Draw()
  {
    this.drawOne.Draw();
  }
}

public class TwoOne : ExampleClassBase
{
  private DrawOne drawOne;
  private DrawTwo drawTwo;

  public One(DrawOne drawOne, DrawTwo drawTwo)
  {
    this.drawOne = drawOne;
    this.drawTwo = drawTwo;
  }

  public void Draw()
  {
    this.drawOne.Draw();
    this.drawTwo.Draw();
  }
}

// the other six classes here

이 접근법은 훨씬 더 장황합니다. 그러나 장점이 있습니다.

버그가 포함 된 다음 클래스를 고려하십시오.

public class ThreeTwoOne : ExampleClassBase
{
  private DrawOne drawOne;
  private DrawTwo drawTwo;
  private DrawThree drawThree;

  public One(DrawOne drawOne, DrawTwo drawTwo, DrawThree drawThree)
  {
    this.drawOne = drawOne;
    this.drawTwo = drawTwo;
    this.drawThree = drawThree;
  }

  public void Draw()
  {
    this.drawOne.Draw();
    this.drawTwo.Draw();
  }
}

누락 된 drawThree.Draw () 호출을 발견하는 것이 얼마나 간단합니까? 그리고 순서가 중요하다면, 드로우 콜의 순서도보고 따라 가기가 매우 쉽습니다.

이 접근법의 단점 :

  • 제시된 8 가지 옵션 중 하나는 모두 별도의 수업이 필요합니다
  • 이것은 더 많은 메모리를 사용합니다
  • 이렇게하면 코드가 표면적으로 커집니다
  • 때때로이 방법은 불가능하지만 약간의 변형이있을 수 있습니다.

이 방법의 장점 :

  • 이러한 각 클래스는 완전히 테스트 가능합니다. 때문에
  • Draw 메소드의 순환 복잡성은 낮습니다 (그리고 이론적으로 DrawOne, DrawTwo 또는 DrawThree 클래스를 조롱 할 수 있습니다)
  • 그리기 방법은 이해할 만합니다-개발자는 방법으로 수행하는 작업을 매듭으로 묶을 필요가 없습니다.
  • 버그는 찾기 쉽고 쓰기가 어렵습니다.
  • 클래스는보다 높은 수준의 클래스로 구성되므로 ThreeThreeThree 클래스를 정의하기가 쉽습니다.

case 문에 복잡한 논리 제어 코드를 작성해야 할 필요가있을 때마다이 (또는 유사한) 접근 방법을 고려하십시오. 미래에 당신은 당신이 기뻐할 것입니다.


이것은 매우 잘 작성된 답변이며 전적으로 접근 방식에 동의합니다. 내 특별한 경우 에이 장황한 것은 각각의 사소한 코드를 고려할 때 무한한 정도로 과잉이 될 것입니다. 그것을 원근법으로 표현하기 위해 코드가 단순히 Console.WriteLine (n)을 수행한다고 상상해보십시오. 그것은 실제로 내가 작업 한 코드와 동일한 기능입니다. 다른 모든 경우의 90 %에서 OOP를 따를 때 솔루션이 가장 적합합니다.
— PerpetualJ

그렇습니다.이 접근법은 모든 상황에서 유용하지는 않습니다. 개발자가 문제를 해결하기위한 하나의 특정 접근 방식으로 고정되는 것이 일반적이기 때문에 여기에 넣고 싶었습니다.
— 스티븐

나는 다른 개발자가 습관적으로 작성한 것으로 확장 성 문제를 해결하려고했기 때문에 실제로 여기에 나온 이유입니다.
— PerpetualJ

0

여기서 스위치를 사용하려는 경우 각 경우를 개별적으로 처리하면 코드가 실제로 더 빠릅니다.

switch(exampleValue)
{
    case One:
        i++;
        break;
    case Two:
        i += 2;
        break;
    case OneAndTwo:
    case Three:
        i+=3;
        break;
    case OneAndThree:
        i+=4;
        break;
    case TwoAndThree:
        i+=5;
        break;
}

각 경우에 단일 산술 연산 만 수행됩니다.

또한 다른 사람들이 gotos 사용을 고려하고 있다면 알고리즘을 다시 생각해야합니다 (C #의 경우가 부족하지만 goto를 사용해야 할 이유는 있음을 인정할 것입니다). Edgar Dijkstra의 유명한 논문 "유해한 문장으로 이동"참조


3
나는 이것이 더 간단한 문제에 직면 한 누군가에게 좋은 아이디어라고 말하고 싶습니다. 게시 해 주셔서 감사합니다. 제 경우에는 그렇게 간단하지 않습니다.
— PerpetualJ

또한, 우리는 오늘날 Edgar가 논문을 작성할 당시 겪었던 문제를 정확히 가지고 있지 않습니다. 오늘날 대부분의 사람들은 코드를 읽기 어렵게 만드는 것 외에는 고토를 미워해야 할 정당한 이유조차 없습니다. 글쎄, 모든 정직에서, 그것이 남용된다면 그렇습니다. 그렇지 않으면 포함 방법에 갇혀서 실제로 스파게티 코드를 만들 수 없습니다. 언어 기능을 해결하기 위해 노력하는 이유는 무엇입니까? goto는 고풍스럽고 99 %의 사용 사례에서 다른 솔루션을 생각해야한다고 말합니다. 그러나 필요한 경우 그것이 바로 거기에 있습니다.
— PerpetualJ

부울이 많으면 확장 성이 떨어집니다.
— user949300

0

당신이 정말로 열거에서 원 모두가 각 단계에 대한 DO / 할-하지 표시, 당신의 세 가지 재 작성 솔루션 이후 특정 예를 들어, if문이 것이 바람직합니다 switch, 그리고 당신이 그것을 허용 대답 만든 것이 좋다 .

당신이 한 좀 더 복잡한 논리했다하지만 하지 그래서 청결 운동을하고 난 여전히 찾을 수 goto에의 switch문이 혼란. 오히려 다음과 같은 것을 볼 것입니다.

switch (enumVal) {
    case ThreeOneTwo: DrawThree; DrawTwo; DrawOne; break;
    case ThreeTwo:    DrawThree; DrawTwo; break;
    case ThreeOne:    DrawThree; DrawOne; break;
    case TwoOne:      DrawTwo; DrawOne; break;
    case Two:         DrawTwo; break;
    default:          DrawOne; break;
}

이것은 완벽하지는 않지만 s보다이 방법이 더 낫다고 생각합니다 goto. 사건의 순서가 너무 길고 서로를 너무 많이 복제하여 각 경우에 대해 완전한 순서를 철자하는 것이 실제로 의미가 없다면 goto코드 중복을 줄이기 보다는 서브 루틴을 선호합니다 .


0

요즘 누군가 goto 키워드를 싫어할 이유가 있는지 확실하지 않습니다. 확실히 구식이며 99 %의 사용 사례에서는 필요하지 않지만 이유 때문에 언어의 기능입니다.

goto키워드 를 싫어하는 이유 는 다음과 같습니다.

if (someCondition) {
    goto label;
}

string message = "Hello World!";

label:
Console.WriteLine(message);

죄송합니다! 분명히 작동하지 않습니다. message변수는 그 코드 경로에 정의되어 있지 않습니다. 따라서 C #은 그것을 통과하지 못합니다. 그러나 암시적일 수 있습니다. 치다

object.setMessage("Hello World!");

label:
object.display();

그리고 그 안에 진술 display이 있다고 가정 WriteLine하십시오.

이러한 종류의 버그는 goto코드 경로를 가리기 때문에 찾기 어려울 수 있습니다 .

이것은 간단한 예입니다. 실제 예가 거의 명확하지 않다고 가정하십시오. label:와 사이에 50 줄의 코드가있을 수 있습니다 message.

언어는 goto사용 가능한 방법을 제한 하고 블록에서 내림차순 으로이 문제를 해결하는 데 도움을 줄 수 있습니다. 그러나 C # goto은 그렇게 제한되지 않습니다. 코드를 뛰어 넘을 수 있습니다. 또한 limit을 제한 goto하려면 이름을 변경하는 것뿐입니다. 다른 언어 break는 숫자 (종료 할 블록) 또는 레이블 (블록 중 하나)이있는 블록에서 내림차순으로 사용 됩니다.

개념은 goto저수준 기계 언어 교육입니다. 그러나 우리가 더 높은 수준의 언어를 사용하는 이유는 가변 범위와 같은 더 높은 수준의 추상화로 제한되기 때문입니다.

당신은 C #을 사용하는 경우 말했다 모든 goto돌며 switch케이스에 케이스에서 이동 문, 합리적 무해합니다. 각 사례는 이미 진입 점입니다. 나는 goto그것이 goto더 위험한 형태 로이 무해한 사용법을 혼란스럽게하기 때문에 그 상황에서 전화하는 것은 어리석은 것이라고 생각합니다 . 나는 그들이 그와 비슷한 것을 사용하기를 훨씬 원합니다 continue. 그러나 어떤 이유로 그들은 언어를 쓰기 전에 저에게 물어 보는 것을 잊었습니다.


2
C#언어 에서는 변수 선언을 뛰어 넘을 수 없습니다. 증거를 위해 콘솔 응용 프로그램을 시작하고 게시물에 제공 한 코드를 입력하십시오. 레이블에 오류가 할당되지 않은 로컬 변수 'message'사용 중임을 나타내는 컴파일러 오류가 발생합니다 . 이것이 허용되는 언어에서는 유효한 문제이지만 C # 언어에서는 그렇지 않습니다.
— PerpetualJ

0

이 많은 선택 (그리고 당신이 말한 것처럼 더 많은 선택)이있을 때 코드가 아니라 데이터 일 수 있습니다.

함수 또는 동작을 나타내는 간단한 열거 형 형식으로 표현 된 액션에 사전 매핑 열거 형 값을 만듭니다. 그런 다음 코드를 간단한 사전 조회로 요약 한 다음 함수 값을 호출하거나 단순화 된 선택을 전환 할 수 있습니다.


-7

루프와 break와 continue의 차이점을 사용하십시오.

do {
  switch (exampleValue) {
    case OneAndTwo: i += 2; break;
    case OneAndThree: i += 3; break;
    case Two: i += 2; continue;
    case TwoAndThree: i += 2;
    // dropthrough
    case Three: i += 3; continue;
    default: break;
  }
  i++;
} while(0);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.