`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.