반환 값 또는 출력 매개 변수 중 어느 것이 더 낫습니까?


147

메소드에서 값을 얻으려면 다음과 같이 반환 값을 사용할 수 있습니다.

public int GetValue(); 

또는:

public void GetValue(out int x);

나는 그들 사이의 차이점을 실제로 이해하지 못하므로 어느 것이 더 좋은지 모릅니다. 설명 해줄 수 있습니까?

감사합니다.


3
예를 들어 C #에 Python과 같은 여러 반환 값이 있기를 바랍니다.
Trap

12
@Trap Tuple원하는 경우 a를 반환 할 수 있지만 일반적인 합의는 둘 이상의 항목을 반환해야하는 경우 일반적으로 관계가 있으며 관계는 일반적으로 클래스로 가장 잘 표현된다는 것입니다.
Pharap

2
C #의 @Pharap Tuples는 현재 형태로 추악하지만 그저 제 의견입니다. 다른 한편으로, "일반 합의"는 유용성과 생산성 관점에서 아무것도 의미하지 않습니다. ref / out 매개 변수로 몇 개의 값을 리턴하는 클래스를 작성하지 않는 것과 같은 이유로 몇 개의 값을 리턴하기위한 클래스를 작성하지 않습니다.
Trap

@Trap return Tuple.Create(x, y, z);추악하지 않습니까. 게다가, 언어 수준으로 소개하는 것은 늦은 시간입니다. ref / out 매개 변수에서 값을 반환하는 클래스를 만들지 않는 이유는 ref / out 매개 변수가 행렬과 같은 큰 가변 구조체 또는 선택적 값에만 실제로 의미가 있고 후자는 논쟁의 여지가 있기 때문입니다.
Pharap

@Pharap C # 팀은 언어 수준에서 튜플을 적극적으로 도입하려고합니다. 익명 유형, .NET Tuple<>및 C # 튜플!. 압도적 인 .NET의 다양한 옵션이 모두 환영합니다 . 방금 C #이 컴파일러 유추 유형 ( autoDlang 과 같은) 을 사용 하는 메소드에서 익명 유형의 반환을 허용하기를 원했습니다 .
nawfal

답변:


153

메서드에 반환 할 다른 항목이없는 경우 반환 값은 거의 항상 올바른 선택입니다. (사실, 난 것 어떤 경우 생각할 수 없다 이제까지 보이드 방법을 원하는 out나는 선택의 여지가있는 경우, 매개 변수입니다. 7의 C # Deconstruct언어 지원 해체하는 방법이 규칙에 매우, 매우 드문 예외로 작용 .)

다른 것 외에도 호출자가 변수를 별도로 선언하지 않아도됩니다.

int foo;
GetValue(out foo);

vs

int foo = GetValue();

Out 값은 다음과 같이 메소드 체인을 방지합니다.

Console.WriteLine(GetValue().ToString("g"));

(실제로, 이는 속성 설정 기의 문제 중 하나이기도하므로 빌더 패턴이 빌더를 리턴하는 메소드를 사용하는 이유입니다 (예 :) myStringBuilder.Append(xxx).Append(yyy).

또한 출력 매개 변수는 리플렉션과 함께 사용하기가 약간 어렵고 일반적으로 테스트도 어렵게 만듭니다. (일반적으로 매개 변수보다 반환 값을 쉽게 조롱하기 위해 더 많은 노력을 기울입니다). 기본적으로 그들이 더 쉽게 만들 수 있다고 생각할 수있는 것은 없습니다 ...

반환 값 FTW.

편집 : 무슨 일인지에 관해서 ...

당신이 "밖으로"매개 변수에 인수를 전달하면 기본적으로, 변수에 전달합니다. (배열 요소도 변수로 분류됩니다.) 호출하는 메소드에는 매개 변수의 스택에 "새"변수가 없습니다. 변수에 저장을 위해 사용합니다. 변수의 모든 변경 사항이 즉시 표시됩니다. 차이점을 보여주는 예는 다음과 같습니다.

using System;

class Test
{
    static int value;

    static void ShowValue(string description)
    {
        Console.WriteLine(description + value);
    }

    static void Main()
    {
        Console.WriteLine("Return value test...");
        value = 5;
        value = ReturnValue();
        ShowValue("Value after ReturnValue(): ");

        value = 5;
        Console.WriteLine("Out parameter test...");
        OutParameter(out value);
        ShowValue("Value after OutParameter(): ");
    }

    static int ReturnValue()
    {
        ShowValue("ReturnValue (pre): ");
        int tmp = 10;
        ShowValue("ReturnValue (post): ");
        return tmp;
    }

    static void OutParameter(out int tmp)
    {
        ShowValue("OutParameter (pre): ");
        tmp = 10;
        ShowValue("OutParameter (post): ");
    }
}

결과 :

Return value test...
ReturnValue (pre): 5
ReturnValue (post): 5
Value after ReturnValue(): 10
Out parameter test...
OutParameter (pre): 5
OutParameter (post): 10
Value after OutParameter(): 10

차이점은 "포스트"단계에서, 즉 로컬 변수 또는 매개 변수가 변경된 후입니다. ReturnValue 테스트에서는 정적 value변수와 아무런 차이가 없습니다 . OutParameter 테스트에서 value변수는 행에 의해 변경됩니다tmp = 10;


2
당신은 반환 가치가 훨씬 낫다고 믿었습니다. :). 그러나 나는 여전히 "깊이"가 어떻게되는지 궁금합니다. 반환 값과 출력 매개 변수는 생성, 할당 및 반환 방식이 다른가요?
Quan Mai

1
tryParse는 out param을 사용하는 것이 적절하고 깨끗한 경우의 가장 좋은 예입니다. 기본적으로 TryParse
Chad Grant

2
도움이되지 않는 대답이 나쁘다. 이 질문 에 대한 의견에서 설명했듯이 매개 변수에는 몇 가지 유용한 장점이 있습니다. 반품과 반품 사이의 선호는 상황에 따라 달라집니다.
aaronsnoswell

2
@aaronsnoswell : 정확한 의견은 무엇입니까? 마음에 곰의 예는 Dictionary.TryGetValue것입니다 하지 그 빈 공간 방법이 아니다으로, 여기에 적용. 반환 값 대신out 매개 변수 원하는 이유를 설명 할 수 있습니까 ? (의 경우에도 TryGetValue개인적으로 모든 출력 정보를 포함하는 반환 값을 선호 ParseResult<T>합니다. 설계 방법에 대한 예는 NodaTime을 참조하십시오 .)
Jon Skeet

2
@Jim : 동의하지 않으면 동의해야 할 것 같습니다. 나는이 함께 머리 사람을 망치에 대한 귀하의 지점을 볼 수 있지만, 그것은 또한 사람들을 위해 덜 친절하게 않는다 그들이 무슨 일을하는지 알고있다. 반환 값이 아닌 예외에 대한 좋은 점은 쉽게 무시 하고 아무 일도없는 것처럼 수행 할 수 없다는 것입니다 ... 반환 값과 out매개 변수를 사용하면 값으로 아무것도 할 수 없습니다.
Jon Skeet

26

더 나은 방법은 특정 상황에 따라 다릅니다. 존재 하는 이유 중 하나out하나의 메소드 호출에서 여러 값을 쉽게 리턴하기위한 것입니다.

public int ReturnMultiple(int input, out int output1, out int output2)
{
    output1 = input + 1;
    output2 = input + 2;

    return input;
}

따라서 하나는 다른 것보다 더 나은 것은 아닙니다. 그러나 일반적으로 위와 같은 상황이 아닌 한 간단한 반환을 사용하려고합니다.

편집 : 키워드가 존재하는 이유 중 하나를 보여주는 샘플입니다. 위의 방법은 최선의 방법으로 간주되지 않습니다.


2
나는 이것에 동의하지 않습니다. 왜 4 개의 정수로 데이터 구조를 반환하지 않습니까? 이것은 정말로 혼란 스럽다.
차드 그랜트

1
분명히 여러 값을 반환하는 더 많은 (더 나은) 방법이 있습니다 .OP가 처음에 왜 존재하는지 이유를 OP에 제공하고 있습니다.
pyrocumulus

2
@Cloud에 동의합니다. 그것이 최선의 방법이 아니기 때문에 그것이 존재해서는 안된다는 것을 의미하지는 않습니다.
Cerebrus

글쎄, 코드는 컴파일되지 않을 것입니다 ... 적어도 나쁜 습관으로 간주되거나 바람직하지 않다고 언급해야합니다.
차드 그랜트

1
컴파일되지 않습니까? 왜 그런데? 이 샘플은 완벽하게 컴파일 할 수 있습니다. 그리고 모범 사례의 교훈이 아닌 특정 구문 / 키워드가 존재하는 이유에 대한 샘플을 제공합니다.
pyrocumulus

23

일반적으로 out 매개 변수보다 반환 값을 선호해야합니다. 두 가지 일을 해야하는 코드를 작성하는 것을 발견하면 아웃 매개 변수는 필요한 악입니다. 이에 대한 좋은 예는 Try 패턴입니다 (예 : Int32.TryParse).

두 메소드의 호출자가해야 할 일을 고려해 봅시다. 첫 번째 예를 위해 이것을 쓸 수 있습니다 ...

int foo = GetValue();

변수를 선언하고 한 줄로 메소드를 통해 할당 할 수 있습니다. 두 번째 예에서는 다음과 같습니다.

int foo;
GetValue(out foo);

이제 변수를 미리 선언하고 두 줄로 코드를 작성해야합니다.

최신 정보

이러한 유형의 질문을 할 때 살펴 봐야 할 곳은 .NET Framework 디자인 지침입니다. 책 버전이있는 경우이 주제 (184-185 페이지)에서 Anders Hejlsberg와 다른 사람들이 주석을 볼 수 있지만 온라인 버전은 여기에 있습니다 ...

http://msdn.microsoft.com/en-us/library/ms182131(VS.80).aspx

API에서 두 가지를 반환 해야하는 경우 struct / class로 래핑하는 것이 out param보다 낫습니다.


큰 대답, 특히 개발자가 (일반적이지 않은) 변수를 사용하도록하는 (공통) 함수 인 TryParse에 대한 참조.
Cerebrus

12

out아직 언급되지 않은 매개 변수 를 사용하는 한 가지 이유가 있습니다 . 호출 메소드가이를 수신해야합니다. 메소드가 호출자가 버려서는 안되는 값을 생성 out하면 호출자가 구체적으로 수락 하도록 강제합니다.

 Method1();  // Return values can be discard quite easily, even accidentally

 int  resultCode;
 Method2(out resultCode);  // Out params are a little harder to ignore

물론 호출자 는 매개 변수 의 을 여전히 무시할 수 out있지만주의를 기울였습니다.

이것은 드문 필요입니다. 더 자주, 실제 문제에 대한 예외를 사용하거나 "FYI"에 대한 상태 정보가있는 오브젝트를 리턴해야하지만 이것이 중요한 상황이있을 수 있습니다.


8

주로 선호

나는 반품을 선호하며 여러 반품이 있으면 결과 DTO에 포장 할 수 있습니다

public class Result{
  public Person Person {get;set;}
  public int Sum {get;set;}
}

5

하나의 반환 값만 가질 수있는 반면 여러 개의 출력 매개 변수를 가질 수 있습니다.

이 경우 매개 변수 만 고려하면됩니다.

그러나 메소드에서 둘 이상의 매개 변수를 반환 해야하는 경우 OO 접근 방식에서 반환되는 것을 보고이 매개 변수를 사용하여 객체 또는 구조체를 반환하는 것이 더 나은지 고려할 수 있습니다. 따라서 다시 반환 값으로 돌아갑니다.


5

거의 항상 반환 값을 사용해야합니다. ' out'매개 변수는 많은 API, 구성 성 등에 약간의 마찰을 일으 킵니다.

가장 주목할만한 예외는 TryParse패턴 과 같이 여러 값을 반환하려는 경우입니다 (.Net Framework에는 4.0까지 튜플이 없음) .


좋은 방법이지만 Arraylist를 사용하여 여러 값을 반환 할 수 있는지 확실하지 않습니다.
BA

@BA 아니오 그것은 좋은 습관이 아닙니다. 다른 사용자는이 Arraylist에 무엇이 포함되어 있는지 또는 그들이 어떤 위치에 있는지 알지 못합니다. 예를 들어 읽은 바이트 양과 값을 반환하려고합니다. 명명 된 튜플이나 튜플이 훨씬 좋습니다. ArrayList 또는 다른 목록은 사물 목록 (예 : 사람 목록)을 반환하려는 경우에만 유용합니다.
sLw

2

이 간단한 예제에서 대신 다음을 선호합니다.

public int Value
{
    get;
    private set;
}

그러나 그것들은 모두 거의 동일합니다. 일반적으로 메소드에서 여러 값을 다시 전달해야하는 경우 'out'만 사용합니다. 메소드 안팎으로 값을 보내려면 'ref'를 선택하십시오. 값을 반환하는 경우 내 방법이 가장 좋지만 매개 변수를 전달하고 값을 다시 얻으려면 첫 번째 선택을 선택하십시오.


2

나는 관리되지 않는 메모리로 작업 할 때 유용 할 수있는 몇 가지 시나리오 중 하나라고 생각합니다. "반환 된"값은 자체적으로 폐기되기를 기대하지 않고 수동으로 폐기해야한다는 것을 분명히하고 싶습니다. .


2

또한 반환 값은 비동기 디자인 패러다임과 호환됩니다.

ref 또는 out 매개 변수를 사용하는 경우 "비동기"기능을 지정할 수 없습니다.

요약하면 반환 값을 사용하면 메서드 체인, 구문이 더 명확 해 지므로 (호출자가 추가 변수를 선언 할 필요가 없음) 향후 실질적인 수정없이 비동기식 디자인이 가능합니다.


"비동기"지정에 대한 좋은 지적. 다른 사람이 그것을 언급했을 것이라고 생각했습니다. 연결-식으로 반환 값 (실제로 함수 자체)을 사용하는 것도 또 다른 핵심입니다. 프로세스에서 물건을 가져 오는 방법 만 고려하는 것 ( "버킷"-튜플 / 클래스 / 구조 토론)과 프로세스 자체를 단일 값으로 대체 할 수있는 표현처럼 처리하는 것 ( 함수 자체는 1 값만 반환하므로).
user1172173

1

둘 다 다른 목적을 가지고 있으며 컴파일러가 동일하게 취급하지 않습니다. 메소드가 값을 리턴해야하는 경우 return을 사용해야합니다. Out은 메서드가 여러 값을 반환해야하는 경우에 사용됩니다.

return을 사용하면 데이터가 먼저 메소드 스택에 기록 된 다음 호출 메소드에 기록됩니다. 출력되는 동안 호출 메소드 스택에 직접 기록됩니다. 더 이상의 차이점이 있는지 확실하지 않습니다.


방법 스택? 저는 C # 전문가는 아니지만 x86은 스레드 당 하나의 스택 만 지원합니다. 반환되는 동안 메서드의 "프레임"이 할당 해제되고 컨텍스트 전환이 발생하면 할당 해제 된 스택을 덮어 쓸 수 있습니다. c에서 모든 반환 값은 레지스트리 eax에 들어갑니다. 객체 / 구조체를 반환하려면 힙에 할당해야하며 포인터는 eax에 배치됩니다.
Stefan Lundström

1

다른 사람들이 말했듯이 : 매개 변수가 아닌 반환 값.

"프레임 워크 디자인 지침"(제 2 판) 책을 추천 할 수 있습니까? 184-185 페이지는 매개 변수를 피하는 이유를 다룹니다. 전체 책은 모든 종류의 .NET 코딩 문제에 대해 올바른 방향으로 안내합니다.

프레임 워크 디자인 가이드 라인과 함께 정적 분석 도구 인 FxCop을 사용합니다. Microsoft 사이트에서 무료로 다운로드 할 수 있습니다. 컴파일 된 코드에서 이것을 실행하고 그것이 말하는 것을보십시오. 그것이 수백과 수백 가지에 대해 불평한다면 ... 당황하지 마십시오! 모든 경우에 대해 말하는 내용을 조용하고 신중하게 살펴보십시오. 최대한 빨리 물건을 고치려고 서두르지 마십시오. 그것이 무엇을 말하고 있는지 배우십시오. 당신은 숙달로 향하게 될 것입니다.


1

반환 유형이 bool 인 out 키워드를 사용하면 코드 팽창이 줄어들고 가독성이 향상되는 경우가 있습니다. 기본적으로 출력 매개 변수의 추가 정보가 무시되는 경우가 있습니다. 예를 들면 다음과 같습니다.

var result = DoThing();
if (result.Success)
{
    result = DoOtherThing()
    if (result.Success)
    {
        result = DoFinalThing()
        if (result.Success)
        {
            success = true;
        }
    }
}

vs :

var result;
if (DoThing(out result))
{
    if (DoOtherThing(out result))
    {
        if (DoFinalThing(out result))
        {
            success = true;
        }
    }
}

1

실제 차이는 없습니다. Out 매개 변수는 C #에 있으며 메서드가 둘 이상의 값을 반환 할 수 있습니다.

그러나 약간의 차이점이 있지만 실제로는 중요하지 않습니다.

out 매개 변수를 사용하면 다음과 같은 두 줄을 사용해야합니다.

int n;
GetValue(n);

반환 값을 사용하면 한 줄로 할 수 있습니다.

int n = GetValue();

또 다른 차이점은 (값 유형에 대해서만 정확하고 C #이 함수를 인라인하지 않는 경우에만) 반환 값을 사용하면 함수가 반환 될 때 반드시 값을 복사하지만 OUT 매개 변수를 사용하면 반드시 그렇게하지는 않습니다.


0

out은 메소드에서 선언 한 객체를 반환하려고 할 때 더 유용합니다.

public BookList Find(string key)
{
   BookList book; //BookList is a model class
   _books.TryGetValue(key, out book) //_books is a concurrent dictionary
                                     //TryGetValue gets an item with matching key and returns it into book.
   return book;
}

0

반환 값 은 메서드에서 반환하는 정상 값입니다.

여기서 같이 아웃 파라미터 잘 및 REF C 번호 2 개 키워드는 그들이 같은 변수를 전달할 수 있도록 기준 .

가장 큰 차이점 심판아웃 이다 심판 하기 전에 초기화되어야 밖으로 하지 않습니다


-2

나는이 질문을 보지 않을 것이라고 생각하지만, 매우 숙련 된 프로그래머이며 더 개방적 인 독자 중 일부가주의를 기울이기를 바랍니다.

VRP (Value-Returning Procedure)가 결정적이고 순수하기 때문에 객체 지향 프로그래밍 언어에 더 적합하다고 생각합니다.

'VRP'는 표현식의 일부로 호출되는 함수의 현대적인 학업 이름으로, 표현식을 평가하는 동안 호출을 개념적으로 대체하는 반환 값을 갖습니다. 예를 들어 x = 1 + f(y)함수 와 같은 문장 f에서 VRP로 사용됩니다.

'결정적'은 함수의 결과가 매개 변수의 값에만 의존한다는 것을 의미합니다. 동일한 매개 변수 값으로 다시 호출하면 동일한 결과를 얻을 수 있습니다.

'순수함'은 부작용이 없음을 의미합니다. 함수를 호출 하면 결과를 계산하는 것 외에는 아무 것도 수행하지 않습니다 . 이는 실제로 중요한 부작용이 없음을 의미하는 것으로 해석 될 수 있으므로 VRP가 호출 될 때마다 디버깅 메시지를 출력하는 경우 (예 : 무시할 수 있음)

따라서 C #에서 함수가 결정적이고 순수하지 않은 경우 void함수 (즉, VRP 아님)로 만들어야하며 반환 해야하는 값은 out또는 ref매개 변수 로 반환해야한다고 말합니다 .

예를 들어, 데이터베이스 테이블에서 일부 행을 삭제하는 기능이 있고 삭제 된 행 수를 리턴하려면 다음과 같이 선언해야합니다.

public void DeleteBasketItems(BasketItemCategory category, out int count);

때때로이 함수를 호출하려고하지만를 얻지 않으려면 count항상 과부하를 선언 할 수 있습니다.

이 스타일이 객체 지향 프로그래밍에 더 적합한 이유 를 알고 싶을 수도 있습니다 . 일반적으로 '프로 시저 프로그래밍'이라고 불릴 수있는 프로그래밍 스타일에 적합하며 객체 지향 프로그래밍에 더 적합한 절차 적 프로그래밍 스타일입니다.

왜? 객체의 고전적인 모델은 속성 (일명 속성)을 가지고 있으며, 해당 속성을 읽고 업데이트하여 (주로) 객체를 조사하고 조작합니다. 절차 적 프로그래밍 스타일은 속성을 가져오고 설정하는 작업간에 임의의 코드를 실행할 수 있기 때문에이 작업을보다 쉽게 ​​수행하는 경향이 있습니다.

절차 적 프로그래밍의 단점은 모든 곳에서 임의의 코드를 실행할 수 있기 때문에 전역 변수와 부작용을 통해 매우 불분명하고 버그에 취약한 상호 작용을 얻을 수 있다는 것입니다.

따라서 간단히 말하면 코드를 읽는 사람에게 함수가 값을 반환하지 않음으로써 부작용을 일으킬 수 있음 을 알리는 것이 좋습니다 .


> 때때로이 함수를 호출하고 개수를 얻지 않으려면 언제든지 과부하를 선언 할 수 있습니다. C # 버전 7 (제 생각에) 이상에서는 _폐기 기호를 사용하여 출력 매개 변수를 무시할 수 있습니다. 예 : DeleteBasketItems (category, out _);
토론자
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.