if / return에 대한 모범 사례


60

if진술을 할 때 돌아 오는 더 좋은 방법이 무엇인지 알고 싶습니다 .

예 1 :

public bool MyFunction()
{
   // Get some string for this example
   string myString = GetString();

   if (myString == null)
   {
      return false;
   }
   else
   {
      myString = "Name " + myString;
      // Do something more here...
      return true;
   }
}

예 2 :

public bool MyFunction()
{
   // Get some string for this example
   string myString = GetString();

   if (myString == null)
   {
      return false;
   }

   myString = "Name " + myString;
   // Do something more here...
   return true;
}

두 예제에서 볼 수 있듯이 함수는 반환 true/false되지만 else첫 번째 예제와 같이 명령문을 넣는 것이 좋습니까? 아니면 넣지 않는 것이 좋습니다?


7
첫 번째 'if'에서 오류 만 확인하는 경우 오류가 실제 논리의 일부로 간주되어서는 안되므로 'else'를 포함하지 않는 것이 좋습니다.
Mert Akcakaya

2
개인적으로 부작용을 일으키는 기능에 대해 더 걱정할 것이지만 이것이 잘 선택되지 않은 예라고 생각합니까?
jk.


14
나를 위해, 첫 번째 버전은 그들의 숙제를 완료하고, 그들이 사라 일단 다음, 학생들의 나머지 말을하지 않았다 방에있는 모든 학생들을 기각 같다 "당신이 경우 지금 않은 숙제를 완료 ...". 말이되지만 불필요합니다. 조건부가 더 이상 조건부가 아니기 때문에을 삭제하는 경향이 else있습니다.
Nicole

1
당신이하고 싶은 '여기에 더 많은 일을하라'는 무엇입니까? 함수 디자인 방식을 완전히 바꿀 수 있습니다.
베네딕트

답변:


81

예 2는 가드 블록이라고합니다. 문제가 발생한 경우 (예 : 잘못된 매개 변수 또는 잘못된 상태) 예외를 일찍 반환하거나 던지는 것이 좋습니다. 정상적인 논리 흐름에서는 예 1을 사용하는 것이 좋습니다.


+1-좋은 구별과 내가 대답하려고하는 것.
Telastyn

3
@ pR0Ps는 아래에 동일한 대답 이 있으며 왜 더 깔끔한 경로인지에 대한 코드 예제를 제공합니다. 좋은 답변 +1.

이 질문은 SO에 대해 여러 번 요청되었으며 논쟁의 여지가 있지만 좋은 대답입니다. 끝에서부터 돌아 오도록 훈련받은 많은 사람들은이 개념에 약간의 문제가 있습니다.
Bill K

2
+1. 가드 블록을 잘못 피하면 화살표 코드가 발생하는 경향이 있습니다.
Brian

두 예제 모두 가드 블록을 보여주지 않습니까?
ernie

44

내 개인 스타일은 if가드 블록에 단일을 사용 하고 실제 메소드 처리 코드에서 if/ else를 사용하는 것입니다.

이 경우 myString == null가드 조건으로 사용하고 있으므로 단일 if패턴 을 사용하는 경향이 있습니다 .

좀 더 복잡한 코드를 고려하십시오.

예 1 :

public bool MyFunction(myString: string){

    //guard block
    if (myString == null){
        return false;
    }
    else{
        //processing block
        myString = escapedString(myString);

        if (myString == "foo"){
            //some processing here
            return false;
        }
        else{
            myString = "Name " + myString;
            //other stuff
            return true;
        }
    }
}

예 2 :

public bool MyFunction(myString: string){

    //guard block
    if (myString == null){
        return false;
    }

    //processing block
    myString = escapedString(myString);

    if (myString == "foo"){
        //some processing here
        return false;
    }
    else{
        myString = "Name " + myString;
        //other stuff
        return true;
    }
}

예 1에서 가드와 나머지 방법은 모두 if/ else형식입니다. 가드 블록이 단일 if형식 인 예제 2와 비교 하고 나머지 방법은 if/ else형식을 사용합니다 . 개인적으로, 예제 2는 이해하기 쉬우 며 예제 1은 지저분하고 들여 쓰기 된 것으로 보입니다.

이것은 고안된 예이며 else if명령문을 사용 하여 정리할 수 있지만 가드 블록과 실제 함수 처리 코드의 차이점을 보여 주려고합니다.

괜찮은 컴파일러는 어쨌든 둘 다에 대해 동일한 출력을 생성해야합니다. 둘 중 하나를 사용하는 유일한 이유는 개인적인 취향이나 기존 코드의 스타일을 따르는 것입니다.


18
예제 2는 다른 것을 제거하면 더 읽기 쉽습니다.
briddums

18

개인적으로, 나는 두 번째 방법을 선호합니다. 나는 그것이 더 짧고 들여 쓰기가 적고 읽기가 쉽다고 생각합니다.


들여 쓰기가 적을수록 +1 이 검사는 여러 번 확인해야하는 경우 불분명합니다.
d.raev

15

내 개인적인 연습은 다음과 같습니다.

  • 종료 점이 여러 개인 함수가 마음에 들지 않으며 유지 관리 및 추적이 어렵다는 것을 알았습니다. 코드 수정은 본질적으로 약간 부주의하기 때문에 내부 논리를 손상시킵니다. 복잡한 계산 일 때 시작 부분에 반환 값을 만들고 끝 부분에 반환합니다. 이렇게하면 각 if-else, switch 등 경로를 신중하게 따르고 올바른 위치에 값을 올바르게 설정해야합니다. 또한 기본 반환 값을 설정할지 또는 시작할 때 초기화되지 않은 상태로 둘지 결정하는 데 약간의 시간을 소비합니다. 이 방법은 논리 또는 반환 값 유형 또는 의미가 변경 될 때도 도움이됩니다.

예를 들면 다음과 같습니다.

public bool myFunction()
{
   // First parameter loading
   String myString = getString();

   // Location of "quick exits", see the second example
   // ...

   // declaration of external resources that MUST be released whatever happens
   // ...

   // the return variable (should think about giving it a default value or not) 
   // if you have no default value, declare it final! You will get compiler 
   // error when you try to set it multiple times or leave uninitialized!
   bool didSomething = false;

   try {
     if (myString != null)
     {
       myString = "Name " + myString;
       // Do something more here...

       didSomething = true;
     } else {
       // get other parameters and data
       if ( other conditions apply ) {
         // do something else
         didSomething = true;
       }
     }

     // Edit: previously forgot the most important advantage of this version
     // *** HOUSEKEEPING!!! ***

   } finally {

     // this is the common place to release all resources, reset all state variables

     // Yes, if you use try-finally, you will get here from any internal returns too.
     // As I said, it is only my taste that I like to have one, straightforward path 
     // leading here, and this works even if you don't use the try-finally version.

   }

   return didSomething;
}
  • 유일한 예외 : 시작시 (또는 드물게는 프로세스 내부에서) "빠른 종료". 실제 계산 논리가 입력 매개 변수와 내부 상태의 특정 조합을 처리 할 수 ​​없거나 알고리즘을 실행하지 않고 쉬운 솔루션이있는 경우 모든 블록을 if 블록으로 캡슐화 (때로는 깊게)하는 데 도움이되지 않습니다. 이것은 핵심 로직의 일부가 아닌 "예외 상태"이므로 감지 한 즉시 계산에서 벗어나야합니다. 이 경우 다른 분기가 없으며 정상적인 조건에서는 단순히 실행이 계속됩니다. 물론, "예외 상태"는 예외를 던져서 더 잘 표현할 수 있지만 때로는 지나치다.

예를 들면 다음과 같습니다.

public bool myFunction()
{
   String myString = getString();

   if (null == myString)
   {
     // there is nothing to do if myString is null
     return false;
   } 

   myString = "Name " + myString;
   // Do something more here...

   // not using return value variable now, because the operation is straightforward.
   // if the operation is complex, use the variable as the previous example.

   return true;
}

"one exit"규칙은 계산에 해제해야하는 외부 리소스가 필요한 경우 또는 함수를 떠나기 전에 재설정해야하는 상태를 나타내는 경우에도 도움이됩니다. 때로는 개발 중에 나중에 추가되기도합니다. 알고리즘 내에 여러 개의 엑시트가 있으면 모든 브랜치를 올바르게 확장하는 것이 훨씬 어렵습니다. (예외가 발생할 경우 드문 예외적 인 경우 부작용을 피하기 위해 릴리스 / 리셋도 finally 블록에 넣어야합니다 ...)

귀하의 사례는 "실제 작업 전에 빠른 종료"범주에 해당하는 것으로 보이며 예 2 버전과 같이 작성합니다.


9
"여러 개의 종료 점이있는 함수가 마음에 들지 않습니다"+1
Corv1nus

@LorandKedves-예를 추가했습니다.
Matthew Flynn

@MatthewFlynn 글쎄, 당신이 그것이 내 대답의 끝에서 제안한 것과 반대되는 것을 신경 쓰지 않는다면 ;-) 예제를 계속하면 마침내 우리 둘 모두에게 좋을 것입니다 :-)
Lorand Kedves

@MatthewFlynn (죄송합니다, 저는 게비 놈입니다. 제 의견을 명확하게 해주셔서 감사합니다. 현재 버전이 마음에 드시기 바랍니다.)
Lorand Kedves

13
종료 점이 여러 개인 함수와 변경 가능한 결과 변수가있는 함수 중에서 전자는 두 가지 악한 것보다 훨씬 적은 것으로 보입니다.
존 퍼디

9

나는 두 가지 이유로 가드 블록을 사용하는 것을 선호합니다.

  1. 특정 조건에서 빠른 종료가 가능합니다.
  2. 나중에 코드에서 복잡하고 불필요한 if 문이 필요하지 않습니다.

일반적으로 말해서 방법의 핵심 기능이 명확하고 최소한 인 방법을 선호합니다. 가드 블록은 시각적으로이를 가능하게합니다.


5

나는 "Fall Through"접근법을 좋아한다 :

public bool MyFunction()
{
   string myString = GetString();

   if (myString != null)
   {
     myString = "Name " + myString;
     return true;
    }
    return false;
}

작업에는 특정 조건이 있으며 그 밖의 모든 것은 기본 "통과"반환입니다.


1

하나의 if 조건이 있으면 스타일에 대해 너무 많은 시간을 소비하지 않을 것입니다. 그러나 여러 가드 조건이 있으면 style2를 선호합니다.

이것을 그림. 테스트가 복잡하고 복잡성을 피하기 위해 하나의 OR 조건으로 묶고 싶지 않다고 가정하십시오.

//Style1
if (this1 != Right)
{ 
    return;
}
else if(this2 != right2)
{
    return;
}
else if(this3 != right2)
{
    return;
}
else
{
    //everything is right
    //do something
    return;
}

//Style 2
if (this1 != Right)
{ 
   return;
}
if(this2 != right2)
{
    return;
}
if(this3 != right2)
{
    return;
}


//everything is right
//do something
return;

여기에 두 가지 주요 장점이 있습니다

  1. 단일 함수의 코드를 두 개의 시각적 로그 블록으로 구분합니다. 위의 유효성 검사 블록 (가드 조건)과 실행 가능한 코드의 아래쪽 블록입니다.

  2. 하나의 조건을 추가 / 제거해야하는 경우 전체 if-elseif-else 사다리를 엉망으로 만들 가능성을 줄입니다.

또 다른 사소한 장점은 치료할 중괄호 세트가 적다는 것입니다.


0

이것은 어떤 소리가 더 좋은지 문제가되어야합니다.

If condition
  do something
else
  do somethingelse

그때 더 잘 표현합니다

if condition
  do something
do somethingelse

작은 방법의 경우 큰 차이가 없지만 복합적인 방법의 경우 제대로 분리되지 않으므로 이해하기가 더 어려워 질 수 있습니다


3
방법이 너무 길어 두 번째 양식을 이해하기 어려운 경우 너무 길다.
케빈 클라인

7
do something반품 이 포함 된 경우에만 두 경우가 동일합니다 . 그렇지 않으면 두 번째 코드는 첫 번째 블록의 작동 방식이 아닌 술어에 do somethingelse관계없이 실행됩니다 if.
Adnan

그들은 또한 OP가 그의 예에서 설명했던 것입니다. 질문의 행을 따라
José Valente

0

값 반환 함수의 끝에 누락 된 return 문이 즉시 "잠깐, 뭔가 잘못되었습니다"-플래그를 트리거하기 때문에 예제 1이 자극적입니다. 따라서 이와 같은 경우 예제 2를 사용합니다.

그러나 오류 처리, 로깅 등과 같은 기능의 목적에 따라 일반적으로 더 관련이 있습니다. 따라서 Lorand Kedves의 답변에 동의하며 비용이 발생하더라도 종료 지점이 하나 있습니다. 추가 플래그 변수. 대부분의 경우 유지 관리 및 이후 확장을 더 쉽게 만듭니다.


0

귀하의 경우 if문이 항상 반환하고 함수의 나머지 코드에 대한 다른 사람을 사용하는 이유가 없습니다. 이렇게하면 줄과 들여 쓰기가 추가됩니다. 불필요한 코드를 추가하면 읽기가 더 어려워지고 누구나 읽을 수 있습니다 .


0

귀하의 예에서, else분명히 불필요합니다. 그러나 ...

코드 줄을 빠르게 훑어 볼 때, 일반적으로 눈은 코드의 흐름에 대한 아이디어를 얻기 위해 중괄호와 들여 쓰기를 봅니다. 실제로 코드 자체에 정착하기 전에 따라서 코드를 작성하고 else두 번째 코드 블록 주위에 중괄호와 들여 쓰기를 사용하면 실제로는 한 블록 또는 다른 블록이 실행되는 "A 또는 B"상황임을 독자가 더 빨리 알 수 있습니다. 즉, 독자는 return처음 이후에 괄호와 들여 쓰기를 봅니다 if.

내 의견으로는, 이것은 코드에 여분의 것을 추가하는 것이 실제로 더 읽기 쉽고 덜 읽을 수있는 드문 경우 중 하나입니다.


0

함수가 뭔가를 반환 한다는 것이 즉시 명백하기 때문에 예제 2를 선호합니다 . 그러나 그 이상으로, 나는 다음과 같이 한곳에서 돌아 오는 것을 선호합니다.

public bool MyFunction()
{
    bool result = false;

    string myString = GetString();

    if (myString != nil) {
        myString = "Name " + myString;

        result = true;
    }

    return result;
}

이 스타일로 나는 할 수 있습니다 :

  1. 내가 무언가를 돌려주고 있음을 즉시보십시오.

  2. 단 하나의 중단 점으로 함수를 호출 할 때마다 결과를 포착하십시오.


이것은 내가 문제에 접근하는 방법과 비슷합니다. 유일한 차이점은 결과 변수를 사용하여 myString 평가를 캡처하여 반환 값으로 사용한다는 것입니다.
척 콘웨이

@ChuckConway 동의하지만 OP의 프로토 타입을 고수하려고했습니다.
Caleb

0

내 개인적인 스타일은가는 경향이있다

function doQuery(string) {
    if (!query(string)) {
        query("ABORT");
        return false;
    } // else
    if(!anotherquery(string) {
        query("ABORT");
        return false;
    } // else
    return true;
}

주석 처리 된 else명령문을 사용하여 프로그램 플로우를 표시하고 읽을 수 있도록 유지하지만 많은 단계가 포함 된 경우 화면을 가로 질러 쉽게 도달 할 수있는 대량 들여 쓰기를 피하십시오.


1
개인적으로, 나는 당신의 코드로 작업하지 않아도되기를 바랍니다.
CVn

여러 검사가있는 경우이 방법이 약간 길어질 수 있습니다. 각 if 절에서 리턴하는 것은 goto 문을 사용하여 코드 섹션을 건너 뛰는 것과 유사합니다.
척 콘웨이

이것이 else포함 된 조항 이있는 코드 와이 코드 중에서 선택했다면 컴파일러가 효과적으로 무시하기 때문에 후자를 선택합니다. 가에 달려 있지만 else if 하지 번 원본보다 더 들여 쓰기를 증가 -이 경우 당신과 함께 동의 할 거라고. 그러나 else그 스타일이 허용된다면 내 선택은 완전히 떨어질 것 입니다.
Mark Hurd

0

나는 쓸 것이다

public bool SetUserId(int id)
{
   // Get user name from id
   string userName = GetNameById(id);

   if (userName != null)
   {
       // update local ID and userName
       _id = id;
       _userNameLabelText = "Name: " + userName;
   }

   return userName != null;
}

내가 돌아올 것이 가장 분명하기 때문에 나는이 스타일을 선호한다 userName != null.


1
문제는 그 스타일을 선호합니까?
Caleb

... 솔직히,이 경우 문자열을 왜 원하는지 잘 모르겠습니다. 왜냐하면 결국 어떤 이유로 든 필요하지 않은 추가 부울 테스트를 추가하는 이유는 말할 것도 없습니다.
Shadur

@Shadur 나는 추가 부울 테스트와 사용되지 않은 myString결과를 알고 있습니다. 방금 OP에서 함수를 복사합니다. 좀 더 실제처럼 보이도록 변경하겠습니다. 부울 테스트는 사소한 것이며 문제가되지 않습니다.
tia

1
@Shadur 우리는 여기서 최적화를하지 않습니다. 내 요점은 내 코드를 쉽게 읽고 유지 관리 할 수있게하는 것이며 개인적으로 하나의 참조 null 검사 비용으로 지불합니다.
tia

1
@tia 나는 당신의 코드의 정신을 좋아합니다. userName을 두 번 평가하는 대신 변수에서 첫 번째 평가를 캡처하여 반환했습니다.
척 콘웨이

-1

내 경험에 따르면, 다른 오류없이 if-return을 수행하거나 (대부분 오류가있는 경우) 모든 가능성에 대해 if-else-if 전체 체인을 수행해야합니다.

이렇게하면 분기에 너무 공상하지 않으려 고합니다. 내가 할 때마다 응용 프로그램 논리를 if로 인코딩하여 유지하기가 더 어려워지기 때문에 (예 : 열거 형 OK 또는 ERR 및 모든 ifs를 쓰는 경우) ! OK <-> ERR 다음에 열거에 세 번째 옵션을 추가하는 것이 엉덩이에 고통이된다는 사실을 이용하십시오.)

예를 들어, "null 인 경우 반환"이 공통적 인 패턴이고 세 번째 가능성 (null / not null 이외)에 대해 걱정할 필요가 없기 때문에 평문을 사용합니다. 앞으로.

그러나 테스트가 데이터에 대해보다 심층적 인 검사를 수행 한 것이면 전체 if-else를 향해 실수를 저지 릅니다.


-1

나는 예제 2에 많은 함정이 있다고 생각합니다. 길을 따라 가면 의도하지 않은 코드가 생길 수 있습니다. 여기서 초점을 맞추는 것은 'myString'변수를 둘러싼 논리를 기반으로합니다. 따라서 명시 적으로 모든 조건 테스트는 알려진기본 / 알 수없는 논리를 설명하는 코드 블록에서 수행해야합니다 .

나중에 코드에서 실수로 출력을 크게 변경 한 예제 2에 도입 된 경우 :

   if (myString == null)
   {
      return false;
   }

   //add some v1 update code here...
   myString = "And the winner is: ";
   //add some v2 update code here...
   //buried in v2 updates the following line was added
   myString = null;
   //add some v3 update code here...
   //Well technically this should not be hit because myString = null
   //but we already passed that logic
   myString = "Name " + myString;
   // Do something more here...
   return true;

내가 가진 생각 else의 값이있는 경우 (반환 블록이 바로 지금 우리가 원래 규칙에 대한 의도였다 논리의 캐릭터가 함께 있기 때문에 모든 로직을 추가 미래 버전의 향상된 기능을 추가했다 프로그래머 것 널 (null)에 대한 검사를 다음 없는).

나는 이것을 다음과 같이 진술하는 Codeplex의 C # 지침 (여기에 링크 : http://csharpguidelines.codeplex.com/ )에 크게 믿습니다 .

"기본 블록 (else)이 비어 있으면 설명 주석을 추가하십시오. 또한 해당 블록에 도달하지 않을 경우 InvalidOperationException을 발생시켜 기존 사례를 통해 발생할 수있는 향후 변경 사항을 발견하십시오. 이는 더 나은 코드를 보장합니다. 코드가 여행 할 수있는 모든 경로가 고려되었습니다. "

나는 이와 같은 논리 블록을 사용하여 모든 코드 경로를 명시 적으로 설명하고 의도하지 않은 논리 결과에 코드를 공개하지 않도록 항상 기본 블록을 추가 (if-else, case : default)하도록하는 것이 좋은 프로그래밍 관행이라고 생각합니다.


예제 2에서 else를 갖는 것이 모든 경우를 고려하지 않는 방법은 무엇입니까? 의도 한 결과는 계속 실행 된 것입니다. 이 경우 else 절을 ​​추가하면 메소드의 복잡성이 증가합니다.
척 콘웨이

나는 모든 관련 논리를 간결하고 결정적인 블록에 포함시키지 않는 능력에 동의하지 않습니다. 문자열을! = null로 설정 하면 myString값 을 조작하는 데 중점을두고 if블록에 이어지는 코드 는 결과 논리입니다. 따라서 특정 값을 조작하는 추가 논리에 중점을두기 때문에 else블록에 캡슐화해야한다고 생각합니다 . 그렇지 않으면 의도하지 않게 논리를 분리하고 의도하지 않은 결과를 초래할 수 있으므로 잠재력이 열립니다. 항상 일어날 수는 없지만 이것은 실제로 모범 사례 의견 스타일의 질문이었습니다.
atconway
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.