void 메서드 내에서 return을 사용하는 것이 나쁜 습관입니까?


92

다음 코드를 상상해보십시오.

void DoThis()
{
    if (!isValid) return;

    DoThat();
}

void DoThat() {
    Console.WriteLine("DoThat()");
}

void 메서드 내에서 반환을 사용해도 괜찮습니까? 성능 저하가 있습니까? 또는 다음과 같은 코드를 작성하는 것이 좋습니다.

void DoThis()
{
    if (isValid)
    {
        DoThat();
    }
}
c#  return  void 

1
내용 : void DoThis () {if (isValid) DoThat (); }
Dscoduc

30
코드를 상상하십니까? 왜? 바로 거기입니다! :-D
STW

이것은 좋은 질문입니다. 저는 항상 return을 사용하는 것이 좋은 습관이라고 생각합니다. 메서드 또는 기능을 종료합니다. 특히 여러 IQueryable <T> 결과를 ​​갖는 LINQ 데이터 마이닝 방법에서 모두 서로 의존합니다. 그들 중 하나가 결과가 없으면 경고하고 종료하십시오.
Cheung

답변:



33

가드를 사용하는 또 다른 큰 이유가 있습니다.

중히 여기다:

void MyFunc(object obj)
{
    if (obj != null)
    {
        obj.DoSomething();
    }
}

대:

void MyFunc(object obj)
{
    if (obj == null)
        return;

    obj.DoSomething();
}

이제 다른 프로그래머가 다음 행을 추가한다고 상상해보십시오. obj.DoSomethingElse ();

void MyFunc(object obj)
{
    if (obj != null)
    {
        obj.DoSomething();
    }

    obj.DoSomethingElse();
}

void MyFunc(object obj)
{
    if (obj == null)
        return;

    obj.DoSomething();
    obj.DoSomethingElse();
}

분명히 이것은 단순한 경우이지만 프로그래머는 첫 번째 (중첩 된 코드) 인스턴스에서 프로그램에 충돌을 추가했습니다. 두 번째 예제 (가드가있는 조기 종료)에서 가드를 통과하면 코드가 의도하지 않은 null 참조 사용으로부터 안전합니다.

물론 훌륭한 프로그래머는 이런 실수를하지 않습니다. 그러나 예방이 치료보다 낫습니다.이 잠재적 인 오류 소스를 완전히 제거하는 방식으로 코드를 작성할 수 있습니다. 중첩은 복잡성을 추가하므로 모범 사례에서는 중첩을 줄이기 위해 코드를 리팩토링하는 것이 좋습니다.


예, 그러나 다른 한편으로 여러 계층의 중첩은 해당 조건과 함께 코드를 버그에 더 쉽게 노출시키고 논리를 추적하기 더 어렵게 만들고 더 중요한 것은 디버그하기 어렵게 만듭니다. 플랫 기능은 덜 사악합니다, IMO.
Skrim

18
나는 감소 된 중첩 을 찬성 하고있다! :-)
Jason Williams

동의합니다. 또한 리팩터링 관점에서 보면 obj가 구조체가되거나 null이되지 않을 것이라고 보장 할 수있는 경우 메서드를 리팩터링하는 것이 더 쉽고 안전합니다.
Phil Cooper

18

나쁜 연습 ??? 절대 안돼. 사실, 유효성 검사가 실패하면 가장 빨리 메서드에서 반환하여 유효성 검사를 처리하는 것이 항상 더 좋습니다. 그렇지 않으면 엄청난 양의 중첩 된 if 및 else가 발생합니다. 일찍 종료하면 코드 가독성이 향상됩니다.

또한 유사한 질문에 대한 응답을 확인하십시오. if-else 대신 return / continue 문을 사용해야합니까?


8

나쁜 습관은 아닙니다 (이미 언급 된 모든 이유로). 그러나 메서드에서 더 많은 수익을 얻을수록 더 작은 논리적 메서드로 분할되어야합니다.


8

첫 번째 예는 가드 문을 사용하는 것입니다. 에서 위키 백과 :

컴퓨터 프로그래밍에서 가드는 해당 분기에서 프로그램 실행을 계속하려면 true로 평가해야하는 부울 식입니다.

메서드 맨 위에 많은 경비원을 두는 것이 완벽하게 이해할 수있는 프로그래밍 방법이라고 생각합니다. 기본적으로 "이 중 하나라도 사실이면이 메서드를 실행하지 마십시오"라고 말합니다.

따라서 일반적으로 다음과 같습니다.

void DoThis()
{
  if (guard1) return;
  if (guard2) return;
  ...
  if (guardN) return;

  DoThat();
}

나는 그것이 훨씬 더 읽기 쉽다고 생각합니다.

void DoThis()
{
  if (guard1 && guard2 && guard3)
  {
    DoThat();
  }
}

3

성능 저하는 없지만 두 번째 코드는 더 읽기 쉽고 유지 관리가 더 쉽습니다.


Russell 나는 당신의 의견에 동의하지 않습니다. 그러나 당신은 그것에 대해 하향 투표해서는 안됩니다. +1하여 균등하게합니다. Btw, 나는 부울 테스트와 한 줄에 빈 줄이 뒤 따르는 것이 무슨 일이 일어나고 있는지에 대한 명확한 표시라고 믿습니다. 예 : 로드리고의 첫 번째 예.
Paul Sasik

나는 이것에 동의하지 않는다. 중첩을 늘려도 가독성이 향상되지는 않습니다. 첫 번째 코드는 완벽하게 이해할 수있는 패턴 인 "guard"문을 사용하는 것입니다.
cdmckay

나도 동의하지 않습니다. 기능을 조기에 제거하는 보호 조항은 일반적으로 오늘날 독자가 구현을 이해하는 데 도움이되는 좋은 것으로 간주됩니다.
Pete Hodgson

2

이 경우 두 번째 예제는 더 나은 코드이지만 void 함수에서 반환하는 것과 관련이 없습니다. 두 번째 코드가 더 직접적이기 때문입니다. 그러나 void 함수에서 반환하는 것은 전적으로 괜찮습니다.


0

완벽하게 괜찮고 '성능 저하'는 없지만 대괄호없이 'if'문을 작성하지 마십시오.

항상

if( foo ){
    return;
}

훨씬 더 읽기 쉽습니다. 그리고 코드의 일부가 그렇지 않은 경우 해당 명령문 내에 있다고 우연히 가정하지 않을 것입니다.


2
읽을 수있는 것은 주관적입니다. imho, 불필요한 코드에 추가 된 것은 읽기 어렵게 만듭니다. 주관적인 의견
찰스 Bretana

10
항상 중괄호를 포함하는 더 좋은 이유는 가독성보다는 안전성에 관한 것입니다. 중괄호가 없으면 나중에 누군가가 if의 일부로 추가 명령문을 필요로하는 버그를 쉽게 수정할 수 있습니다. 충분히주의를 기울이지 않고 중괄호를 추가하지 않고 추가합니다. 항상 중괄호를 포함하면 이러한 위험이 제거됩니다.
Scott Dorman

2
실키, {. 이것은 가독성을 크게 향상시키는 동일한 열에 귀하 {와 일치합니다 }(해당하는 열림 / 닫힘 중괄호를 찾기가 훨씬 쉽습니다).
Imagist

1
@Imagist 저는 개인적 취향에 맡길 것입니다. 그리고 그것은 내가 선호하는 방식으로 이루어집니다 :)
Noon Silk

1
모든 닫는 중괄호가 동일한 수준의 indent에 배치 된 여는 중괄호와 일치하면 if닫는 중괄호가 필요한 문 을 시각적으로 구별하는 것이 쉬울 것이므로 if문이 단일 문을 제어하도록하는 것이 안전합니다. 여는 중괄호를와 함께 줄로 다시 밀면 if각 다중 문에 세로 공간 줄이 절약 if되지만 그렇지 않으면 불필요한 닫는 중괄호 줄을 사용해야합니다.
supercat

0

나는 이것에 대한 당신의 모든 젊은 whippersnappers에 동의하지 않을 것입니다.

방법의 중간에 반환을 사용하는 것은 매우 나쁜 습관입니다. ", Dahl, Dijkstra 및 Hoare의"구조적 프로그래밍 "에서 계속됩니다.

기본 규칙은 모든 제어 구조와 모든 모듈에 정확히 하나의 입구와 출구가 있어야한다는 것입니다. 모듈 중간에 명시 적으로 반환하면 해당 규칙이 위반되고 프로그램 상태에 대해 추론하기가 훨씬 더 어려워 져 프로그램이 올바른지 여부를 말하기가 훨씬 더 어려워집니다 (이는 훨씬 더 강력한 속성입니다. "작동 여부"보다).

"GOTO 문은 유해한 것으로 간주 됨"및 "구조적 프로그래밍"이 1970 년대의 "구조적 프로그래밍"혁명을 시작했습니다. 이 두 부분은 오늘날 우리가 if-then-else, while-do 및 기타 명시 적 제어 구조를 가지고있는 이유와 고수준 언어의 GOTO 문이 멸종 위기에 처한 종 목록에있는 이유입니다. (내 개인적인 의견은 그들이 멸종 종 목록에 있어야한다는 것입니다.)

첫 번째 시도에서 허용 테스트를 통과 한 최초의 군사 소프트웨어 인 Message Flow Modulator는 편차, 면제 또는 "예,하지만"말이없는 언어로 작성되었다는 점은 주목할 가치가 있습니다. GOTO 문.

또한 Nicklaus Wirth가 Oberon 프로그래밍 언어의 최신 버전 인 Oberon-07에서 RETURN 문의 의미를 변경하여 형식화 된 프로 시저 (즉, 함수) 선언의 후행 부분이되었음을 언급 할 가치가 있습니다. 함수 본문의 실행 가능한 문. 변화의 그의 해설은 이전의 형태 때문에 그가 정확하게 해냈다 고 말했다 WAS 구조화 프로그래밍의 한 출구 원칙의 위반.


2
@John : 우리는 Pascal (우리 대부분이 어쨌든)을 극복 할 즈음에 다중 수익에 대한 Dykstra 금지 명령을 극복했습니다.
John Saunders

여러 번의 반품이 필요한 경우는 메서드가 너무 많은 일을하려고하므로 줄여야한다는 신호입니다. 나는 이것으로 John까지 가지 않을 것이며 매개 변수 유효성 검사의 일부인 return 문은 합리적인 예외 일 수 있지만 아이디어가 어디에서 왔는지 알 수 있습니다.
kyoryu

@nairdaen : 그 분기의 예외에 대해서는 여전히 논란이 있습니다. 내 가이드 라인은 다음과 같습니다. 개발중인 시스템이 원래 예외적 조건을 유발 한 문제를 수정해야하고 해당 코드를 작성해야하는 사람을 화나게하는 데 신경 쓰지 않는다면 예외를 던질 것입니다. 그런 다음 회의에서 소리를 지르는 이유는 그 사람이 예외를 포착하지 않고 앱이 테스트 중에 충돌했기 때문입니다. 그리고 그가 문제를 해결해야하는 이유를 설명하면 상황이 다시 해결됩니다.
John R. Strohm

가드 진술과 gotos 사이에는 큰 차이가 있습니다. gotos의 나쁜 점은 어디든 점프 할 수 있다는 것입니다. 그래서 풀고 기억하기가 매우 혼란 스러울 수 있습니다. Guard 문은 정반대입니다. 메서드에 대한 게이트 된 항목을 제공 한 후 "안전한"환경에서 작업하고 있음을 알고 나머지 코드를 작성할 때 고려해야 할 사항의 수를 줄입니다 (예 : "이 포인터는 절대 null이 될 수 없다는 것을 알고 있으므로 코드 전체에서이 경우를 처리 할 필요가 없습니다.").
Jason Williams

@Jason : 원래 질문은 가드 문에 관한 것이 아니라 메서드 중간에있는 임의의 반환 문에 관한 것이 었습니다. 주어진 예는 경비원처럼 보였다. 핵심 문제는 반환 사이트에서 메서드가 수행 한 작업과 수행하지 않은 작업에 대해 추론 할 수 있기를 원하고, 임의의 GOTO가 더 어렵게 만드는 것과 똑같은 이유 때문에 무작위 반환이이를 더 어렵게 만든다는 것입니다. 참조 : Dijkstra, "유해한 GOTO 문". 구문 측면에서 cdmckay는 또 다른 대답으로 가드에 대해 선호하는 구문을 제공했습니다. 나는 어떤 형식이 더 읽기 쉬운 지에 대한 그의 의견에 동의하지 않습니다.
John R. Strohm

0

경비원을 사용하는 동안 독자를 혼동하지 않도록 특정 지침을 따르십시오.

  • 함수 는 한 가지를 수행합니다.
  • 가드는 함수 의 첫 번째 논리 로만 도입됩니다.
  • 중첩되지 부분은 함수의 포함 핵심 의도를

// guards point you to the core intent
void Remove(RayCastResult rayHit){

  if(rayHit== RayCastResult.Empty)
    return
    ;
  rayHit.Collider.Parent.Remove();
}

// no guards needed: function split into multiple cases
int WonOrLostMoney(int flaw)=>
  flaw==0 ? 100 :
  flaw<10 ? 30 :
  flaw<20 ? 0 :
  -20
;

-3

객체가 null 인 경우 아무것도 반환하지 않고 예외를 throw합니다.

귀하의 메서드는 객체가 null이 아닐 것으로 예상하고 케이스가 아니므로 예외를 throw하고 호출자가 처리하도록해야합니다.

그러나 조기 복귀는 그렇지 않으면 나쁜 습관이 아닙니다.


1
대답은 질문에 대한 대답이 아닙니다. 질문은 void 메서드이므로 아무것도 반환되지 않습니다. 또한 메서드에는 매개 변수가 없습니다. 반환 유형이 객체이지만이 질문에 적용되지 않는 경우 null을 반환하지 않는다는 요점을 얻습니다.
Luke Hammer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.