메소드 호출자에게 여러 값 반환


답변:


609

C # 7 이상에서는 이 답변을 참조하십시오 .

이전 버전에서는 .NET 4.0+의 Tuple을 사용할 수 있습니다 .

예를 들어 :

public Tuple<int, int> GetMultipleValue()
{
     return Tuple.Create(1,2);
}

두 값 한과 튜플 Item1Item2속성으로.


8
Item1, Item2 대신 명명 된 출력 값을 사용할 수 있다면 매우 좋을 것입니다. C # 7은 아마도 그것을 제공 할 것입니다 .
Sнаđошƒӽ ӽ

1
Sнаđошƒаӽ이 절대적으로 바로 이것이 구문 등을 이용하여 다가오는 C # 7.0에서 지원 될 것으로 예상된다 @ : public (int sum, int count) GetMultipleValues() { return (1, 2); }이 예에서 찍은 이에 우리의 문서 주제 예 .
Jeppe Stig Nielsen

435

C # 7이 릴리스되었으므로 포함 된 새로운 Tuples 구문을 사용할 수 있습니다.

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}

다음과 같이 사용할 수 있습니다.

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");

요소에 이름을 제공 할 수도 있습니다 ( "Item1", "Item2"등이 아님). 서명 또는 리턴 메소드에 이름을 추가하여이를 수행 할 수 있습니다.

(string first, string middle, string last) LookupName(long id) // tuple elements have names

또는

return (first: first, middle: middle, last: last); // named tuple elements in a literal

그것들은 또한 해체 될 수 있는데, 이것은 아주 좋은 새로운 기능입니다 :

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration

체크 아웃 이 링크를 무엇을 할 수 있는지에 더 많은 예제를 볼 수 :)


11
.NET Framework 4.7 또는 .NET Core 2.0 이전을 대상으로 하는 경우 NuGet 패키지설치 해야 합니다 .
Phil

1
결과를 얻으려면 "var result = LookupName (5); Console.WriteLine (result.middle)"을 수행하면됩니다.
alansiqueira27

204

세 가지 다른 방법을 사용할 수 있습니다

1. ref / out 파라미터

심판 사용 :

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    int add = 0;
    int multiply = 0;
    Add_Multiply(a, b, ref add, ref multiply);
    Console.WriteLine(add);
    Console.WriteLine(multiply);
}

private static void Add_Multiply(int a, int b, ref int add, ref int multiply)
{
    add = a + b;
    multiply = a * b;
}

밖으로 사용 :

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    int add;
    int multiply;
    Add_Multiply(a, b, out add, out multiply);
    Console.WriteLine(add);
    Console.WriteLine(multiply);
}

private static void Add_Multiply(int a, int b, out int add, out int multiply)
{
    add = a + b;
    multiply = a * b;
}

2. 구조체 / 클래스

구조체를 사용하여 :

struct Result
{
    public int add;
    public int multiply;
}
static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.add);
    Console.WriteLine(result.multiply);
}

private static Result Add_Multiply(int a, int b)
{
    var result = new Result
    {
        add = a * b,
        multiply = a + b
    };
    return result;
}

수업 사용 :

class Result
{
    public int add;
    public int multiply;
}
static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.add);
    Console.WriteLine(result.multiply);
}

private static Result Add_Multiply(int a, int b)
{
    var result = new Result
    {
        add = a * b,
        multiply = a + b
    };
    return result;
}

3. 튜플

튜플 클래스

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.Item1);
    Console.WriteLine(result.Item2);
}

private static Tuple<int, int> Add_Multiply(int a, int b)
{
    var tuple = new Tuple<int, int>(a + b, a * b);
    return tuple;
}

C # 7 튜플

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    (int a_plus_b, int a_mult_b) = Add_Multiply(a, b);
    Console.WriteLine(a_plus_b);
    Console.WriteLine(a_mult_b);
}

private static (int a_plus_b, int a_mult_b) Add_Multiply(int a, int b)
{
    return(a + b, a * b);
}

3
내 자신의 호기심을 위해서, 당신은 어느 것이 가장 빠르고 '모범 사례'라고 말할 것입니까?
Netferret

'struct 사용'의 가장 좋은 예 :)
SHEKHAR SHETE

1
c # 7 구문을 추가 할 것을 제안하고 (더 많은 사람들이 이것을 투표 :))
twomm November

정보의 경우, 작은 (무관 한) 오타 : 구조체 / 클래스 솔루션에서 더하기 / 곱하기를 섞었습니다.
Szak1

C # Tuple 구문이 무엇인지 몰랐습니다! 몇 년 후에도 새로운 것을 배우십시오!
jhaagsma

75

C #에서는이 작업을 수행 할 수 없습니다. 당신이 할 수있는 것은 out매개 변수가 있거나 자신의 클래스를 반환하는 것입니다 (또는 불변의 경우 구조체).

매개 변수 사용
public int GetDay(DateTime date, out string name)
{
  // ...
}
커스텀 클래스 (또는 구조체) 사용
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

public class DayOfWeek
{
  public int Day { get; set; }
  public string Name { get; set; }
}

24
이 경우의 대안은 반환 유형에 클래스 대신 구조체를 사용하는 것입니다. 반환 값이 상태가없고 일시적인 경우 struct가 더 좋습니다.
Michael Meadows

1
async방법으로 는 불가능합니다 . Tuple갈 길입니다. ( out하지만 동기 작업에서 매개 변수를 사용 합니다. 이러한 경우에는 실제로 유용합니다.)
Codefun64

5
이것은 C # 7에서 가능합니다 : (int, int) Method () {return (1, 2); }
Spook

4
답변을 업데이트해야합니다. 최신 버전의 C #에서 잘못 표시되었습니다. 업데이트되면 downvote를 upvote로 변경합니다.
whitneyland

레거시 코드베이스에서 작업하면서 사용자 정의 클래스를 반환하는 것이 확실한 접근법이었습니다.
브랜트

38

여러 값을 반환하려는 경우 반환하려는 값이 포함 된 클래스 / 구조를 반환하거나 다음과 같이 매개 변수에 "out"키워드를 사용할 수 있습니다.

public void Foo(int input, out int output1, out string output2, out string errors) {
    // set out parameters inside function
}

2
"out"또는 "ref"를 사용하는 것이 좋지 않다고 생각합니다 .— 귀하의 클래스 유형의 반환 값으로 완전히 대체 될 수 있기 때문입니다. "ref"를 사용하는 경우 이러한 매개 변수에 할당하는 방법을 참조하십시오. (내부 코딩 방법에 따라 다릅니다). 함수 본문에서 작성자가 "ref"를 사용하여 인스턴스를 매개 변수에 "새로"추가 한 경우 "널링 가능"값을 전달할 수 있습니다. 그렇지 않으면 그렇지 않습니다. 그래서 그것은 약간 앰비언트입니다. 그리고 우리는 더 나은 방법이 있습니다

33

이전 포스터가 맞습니다. C # 메서드에서 여러 값을 반환 할 수 없습니다. 그러나 몇 가지 옵션이 있습니다.

  • 여러 멤버를 포함하는 구조체를 반환
  • 클래스의 인스턴스를 반환
  • 출력 매개 변수 사용 ( out 또는 ref 키워드 사용)
  • 사전 또는 키-값 쌍을 출력으로 사용

여기의 장단점은 종종 파악하기가 어렵습니다. 구조체를 반환하는 경우 구조체는 값 형식이며 스택에 전달되므로 크기가 작아야합니다. 클래스의 인스턴스를 반환하면 문제를 피하기 위해 사용할 수있는 디자인 패턴이 있습니다 .C #은 참조로 개체를 전달하기 때문에 클래스 멤버를 수정할 수 있습니다 (VB에서와 같이 ByVal이 없기 때문에) ).

마지막으로 출력 매개 변수를 사용할 수 있지만 몇 개 (예 : 3 이하)의 매개 변수 만있는 경우 시나리오에이 사용을 제한합니다. 그렇지 않으면 유지 관리가 어려워집니다. 또한 출력 매개 변수를 사용하면 반환 값에 무언가를 추가해야 할 때마다 메서드 서명이 변경되어야하지만 구조체 또는 클래스 인스턴스를 반환하면 메서드 서명을 수정하지 않고 멤버를 추가 할 수 있기 때문에 민첩성을 저해 할 수 있습니다.

아키텍처 관점에서 키-값 쌍 또는 사전을 사용하지 않는 것이 좋습니다. 이 스타일의 코딩에는 메서드를 사용하는 코드에서 "비밀 지식"이 필요합니다. 키의 의미와 값의 의미를 미리 알고 있어야하며 내부 구현 작업을 수행하는 개발자가 사전 또는 KVP 작성 방식을 변경하면 전체 애플리케이션에서 장애 단계를 쉽게 작성할 수 있습니다.


그리고 Exception반환하려는 두 번째 값이 첫 번째 값과 이질적인 경우 if를 던질 수 있습니다 .
Cœur

21

클래스 인스턴스 를 반환 하거나 매개 변수를 사용 합니다 . 다음은 출력 매개 변수의 예입니다.

void mymethod(out int param1, out int param2)
{
    param1 = 10;
    param2 = 20;
}

다음과 같이 호출하십시오.

int i, j;
mymethod(out i, out j);
// i will be 20 and j will be 10

3
할 수 있다고해서 반드시 그렇게해야한다는 것은 아닙니다. 이것은 대부분의 경우 .Net에서 나쁜 습관으로 널리 받아 들여지고 있습니다.
Michael Meadows

4
왜 이것이 나쁜 실천인지 정교하게 설명 할 수 있습니까?
Zo 님이

C / C ++에서는 나쁜 습관입니다. 문제는 "부작용에 의한 프로그래밍"입니다 : int GetLength (char * s) {int n = 0; while (s [n]! = '\ 0') n ++; s [1] = 'X'; 귀환 (n); } int main () {char greeting [5] = { 'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (인사말); cout << len << ":"<< 인사말; // 출력 : 5 : HXlp} C #에서는 다음과 같이 작성해야합니다. int len ​​= GetLength (ref greeting) "이걸 호출하면 인사말이 같지 않을 것"이라는 큰 경고 플래그를 나타냅니다. 버그를 줄입니다.
Dustin_00

19

많은 방법이 있습니다. 그러나 새로운 객체 또는 구조 또는 이와 같은 것을 만들고 싶지 않으면 C # 7.0 이후에 다음과 같이 할 수 있습니다 .

 (string firstName, string lastName) GetName(string myParameter)
    {
        var firstName = myParameter;
        var lastName = myParameter + " something";
        return (firstName, lastName);
    }

    void DoSomethingWithNames()
    {
        var (firstName, lastName) = GetName("myname");

    }

13

C # 7에는 새로운 Tuple구문이 있습니다.

static (string foo, int bar) GetTuple()
{
    return ("hello", 5);
}

이것을 레코드로 반환 할 수 있습니다.

var result = GetTuple();
var foo = result.foo
// foo == "hello"

새로운 해체 구문을 사용할 수도 있습니다.

(string foo) = GetTuple();
// foo == "hello"

그러나 직렬화에주의,이 모든 문법 설탕입니다 - 실제 컴파일 된 코드에서이 될 것이다 Tuple<string, int>(같은 허용 대답 당 과) Item1Item2대신 foo하고 bar. 즉, 직렬화 (또는 직렬화 해제)는 해당 속성 이름을 대신 사용합니다.

따라서 직렬화의 경우 레코드 클래스를 선언하고 대신 해당 클래스를 리턴하십시오.

C # 7의 새로운 기능은 out매개 변수에 대한 향상된 구문입니다 . 이제 out인라인을 선언 할 수 있는데 , 이는 일부 상황에 더 적합합니다.

if(int.TryParse("123", out int result)) {
    // Do something with result
}

그러나 대부분 자신의 함수가 아닌 .NET의 자체 라이브러리에서 이것을 사용합니다.


타겟팅하는 .Net 버전에 따라 Nuget 패키지 System.ValueTuple을 설치해야 할 수도 있습니다.
Licht

나는 위와 같이 대답하려고했다 ;-)
Jeyara

12

일부 답변은 매개 변수를 사용 하는 것이 좋지만 비동기 메서드에서는 작동하지 않기 때문에 사용하지 않는 것이 좋습니다 . 자세한 내용은 이것을 참조하십시오.

다른 답변은 Tuple을 사용하여 언급했지만 C # 7.0에 도입 된 새로운 기능을 사용하는 것이 좋습니다.

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");

자세한 내용은 여기를 참조하십시오 .


11

이를 수행하는 몇 가지 방법이 있습니다. ref매개 변수 를 사용할 수 있습니다 .

int Foo(ref Bar bar) { }

이것은 함수에 대한 참조를 전달하여 함수가 호출 코드의 스택에서 객체를 수정할 수 있도록합니다. 이것은 기술적으로 "반환 된"값이 아니지만 함수가 비슷한 것을 수행하도록하는 방법입니다. 위의 코드에서 함수는 intand (잠재적으로) modify를 반환 합니다 bar.

또 다른 유사한 방법은 out매개 변수 를 사용하는 것 입니다. out파라미터는 동일 ref추가적인 컴파일러 시행 규칙 파라미터. 이 규칙은 out매개 변수를 함수에 전달하면 반환하기 전에 해당 함수가 값을 설정해야한다는 것입니다. 이 규칙 외에도 out매개 변수 는 매개 변수와 동일하게 작동합니다 ref.

마지막 접근 방식 (및 대부분의 경우 가장 좋은 방법)은 두 값을 모두 캡슐화하고 함수가 다음을 반환하도록 허용하는 형식을 만드는 것입니다.

class FooBar 
{
    public int i { get; set; }
    public Bar b { get; set; }
}

FooBar Foo(Bar bar) { }

이 최종 접근 방식은 더 단순하고 읽기 쉽고 이해하기 쉽습니다.


11

아니요, 적어도 Python에서는 수행 할 수있는 방식이 아닌 C # (C # 7보다 낮은 버전의 경우)의 함수에서 여러 값을 반환 할 수 없습니다.

그러나 몇 가지 대안이 있습니다.

여러 값이 포함 된 객체 유형의 배열을 반환 할 수 있습니다.

private object[] DoSomething()
{
    return new [] { 'value1', 'value2', 3 };
}

out매개 변수 를 사용할 수 있습니다 .

private string DoSomething(out string outparam1, out int outparam2)
{
    outparam1 = 'value2';
    outparam2 = 3;
    return 'value1';
}

10

C # 4에서는 튜플에 대한 내장 지원을 사용하여이를 쉽게 처리 할 수 ​​있습니다.

그 동안 두 가지 옵션이 있습니다.

먼저 ref 또는 out 매개 변수를 사용하여 매개 변수에 값을 할당하면 호출 루틴으로 다시 전달됩니다.

이것은 다음과 같습니다

void myFunction(ref int setMe, out int youMustSetMe);

둘째, 반환 값을 구조 또는 클래스로 마무리하고 해당 구조의 멤버로 다시 전달할 수 있습니다. KeyValuePair는 2에서 잘 작동합니다. 2 이상에서는 사용자 정의 클래스 또는 구조체가 필요합니다.


7

이 "KeyValuePair"를 사용해보십시오

private KeyValuePair<int, int> GetNumbers()
{
  return new KeyValuePair<int, int>(1, 2);
}


var numbers = GetNumbers();

Console.WriteLine("Output : {0}, {1}",numbers.Key, numbers.Value);

출력 :

출력 : 1, 2


5

클래스, 구조, 컬렉션 및 배열은 여러 값을 포함 할 수 있습니다. 함수에서 출력 및 기준 파라미터를 설정할 수도 있습니다. 튜플을 사용하여 동적 및 기능적 언어에서 여러 값을 반환 할 수 있지만 C #에서는 반환 할 수 없습니다.


4

주로 두 가지 방법이 있습니다. 1. out / ref 파라미터 사용 2. 객체 배열 반환


튜플과 튜플의 구문 설탕으로 여러 반환 값이 있습니다.
ANeves

4

기본 Two방법 은 다음과 같습니다 .

1) out매개 변수 로 ' ' 사용 4.0 및 부 버전 모두에 대해 'out'을 사용할 수 있습니다.

'out'의 예 :

using System;

namespace out_parameter
{
  class Program
   {
     //Accept two input parameter and returns two out value
     public static void rect(int len, int width, out int area, out int perimeter)
      {
        area = len * width;
        perimeter = 2 * (len + width);
      }
     static void Main(string[] args)
      {
        int area, perimeter;
        // passing two parameter and getting two returning value
        Program.rect(5, 4, out area, out perimeter);
        Console.WriteLine("Area of Rectangle is {0}\t",area);
        Console.WriteLine("Perimeter of Rectangle is {0}\t", perimeter);
        Console.ReadLine();
      }
   }
}

산출:

사각형의 면적은 20

직사각형의 둘레는 18

* 참고 : * out-keyword는 실제 변수 위치가 호출 된 메소드의 스택에 복사되어 동일한 위치를 다시 쓸 수있는 매개 변수를 설명합니다. 이것은 호출 메소드가 변경된 매개 변수에 액세스 함을 의미합니다.

2) Tuple<T>

튜플의 예 :

사용하여 여러 DataType 값 반환 Tuple<T>

using System;

class Program
{
    static void Main()
    {
    // Create four-item tuple; use var implicit type.
    var tuple = new Tuple<string, string[], int, int[]>("perl",
        new string[] { "java", "c#" },
        1,
        new int[] { 2, 3 });
    // Pass tuple as argument.
    M(tuple);
    }

    static void M(Tuple<string, string[], int, int[]> tuple)
    {
    // Evaluate the tuple's items.
    Console.WriteLine(tuple.Item1);
    foreach (string value in tuple.Item2)
    {
        Console.WriteLine(value);
    }
    Console.WriteLine(tuple.Item3);
    foreach (int value in tuple.Item4)
    {
        Console.WriteLine(value);
    }
    }
}

산출

perl
java
c#
1
2
3

참고 : Tuple 사용은 Framework 4.0 이상에서 유효합니다 . Tuple유형은입니다 class. 메모리에서 관리되는 힙의 별도 위치에 할당됩니다. 를 만든 후에 Tuple는의 값을 변경할 수 없습니다 fields. 이것은 Tuple더 비슷합니다 struct.


4
<--Return more statements like this you can --> 

public (int,string,etc) Sample( int a, int b)  
{
    //your code;
    return (a,b);  
}

당신은 같은 코드를받을 수 있습니다

(c,d,etc) = Sample( 1,2);

나는 그것이 작동하기를 바랍니다.


3

델리게이트를받는 메소드는 호출자에게 여러 값을 제공 할 수 있습니다. 이것은 내 대답 에서 빌려 왔으며 Hadas의 대답 에서 약간 사용되었습니다 .

delegate void ValuesDelegate(int upVotes, int comments);
void GetMultipleValues(ValuesDelegate callback)
{
    callback(1, 2);
}

호출자는 람다 (또는 명명 된 함수)를 제공하고 대리자의 변수 이름을 복사하여 인텔리전스 지원을 제공합니다.

GetMultipleValues((upVotes, comments) =>
{
     Console.WriteLine($"This post has {upVotes} Up Votes and {comments} Comments.");
});

2

OOP 방식으로 다음과 같은 클래스를 사용하십시오.

class div
{
    public int remainder;

    public int quotient(int dividend, int divisor)
    {
        remainder = ...;
        return ...;
    }
}

함수 멤버는 대부분의 호출자가 주로 관심을 갖는 몫을 리턴합니다. 또한 나머지는 데이터 멤버로 저장하며, 나중에 호출자가 쉽게 액세스 할 수 있습니다.

이 방법을 사용하면 많은 추가 "반환 값"을 가질 수 있으며, 많은 오류 메시지가 필요할 수 있지만 오류가 발생할 경우에만 데이터베이스 또는 네트워킹 호출을 구현할 때 매우 유용합니다.

OP가 참조하는 C ++ 질문 에도이 솔루션을 입력했습니다.


2

에서 게시물 위에서 말했듯이 기사, 당신은 세 가지 옵션을 사용할 수 있습니다.

KeyValuePair 가 가장 빠른 방법입니다.

아웃 제이다.

튜플 이 가장 느립니다.

어쨌든 이것은 시나리오에 가장 적합한 것에 달려 있습니다.


2

C #의 향후 버전에는 명명 된 튜플이 포함될 예정입니다. 데모 https://channel9.msdn.com/Events/Build/2016/B889에 대한이 channel9 세션을 살펴보십시오

튜플은 13:00으로 건너 뜁니다. 이것은 다음과 같은 것들을 허용합니다 :

(int sum, int count) Tally(IEnumerable<int> list)
{
// calculate stuff here
return (0,0)
}

int resultsum = Tally(numbers).sum

(비디오에서 불완전한 예)


2

동적 객체를 사용할 수 있습니다. Tuple보다 가독성이 더 좋다고 생각합니다.

static void Main(string[] args){
    var obj = GetMultipleValues();
    Console.WriteLine(obj.Id);
    Console.WriteLine(obj.Name);
}

private static dynamic GetMultipleValues() {
    dynamic temp = new System.Dynamic.ExpandoObject();
    temp.Id = 123;
    temp.Name = "Lorem Ipsum";
    return temp;
}

3
컴파일 시간 유형 검사가 손실됩니다.
Micha Wiedenmann

1

그것을하는 방법 :

1) KeyValuePair (최고 성능-0.32ns) :

    KeyValuePair<int, int> Location(int p_1, int p_2, int p_3, int p_4)
    {                 
         return new KeyValuePair<int,int>(p_2 - p_1, p_4-p_3);
    }

2) 튜플-5.40ns :

    Tuple<int, int> Location(int p_1, int p_2, int p_3, int p_4)
    {
          return new Tuple<int, int>(p_2 - p_1, p_4-p_3);
    }

3) out (1.64 ns) 또는 ref 4) 나만의 커스텀 클래스 / 구조체 만들기

ns-> 나노초

참조 : 다중 반환 값 .


0

당신은 이것을 시도 할 수 있습니다

public IEnumerable<string> Get()
 {
     return new string[] { "value1", "value2" };
 }

1
실제로 여러 값을 반환하지는 않습니다 . 단일 컬렉션 값을 반환합니다.
Matthew Haugen

또한 yield return "value1"; yield return "value2";명시 적으로 새 것을 만들 필요가없는 as를 사용하지 않는 이유 는 string[]무엇입니까?
토마스 플린 코우

0

OperationResult를 사용할 수도 있습니다

public OperationResult DoesSomething(int number1, int number2)
{
// Your Code
var returnValue1 = "return Value 1";
var returnValue2 = "return Value 2";

var operationResult = new OperationResult(returnValue1, returnValue2);
return operationResult;
}

-7

배열 유형에 대한 빠른 답변은 다음을 반환합니다.

private int[] SumAndSub(int A, int B)
{
    return new[] { A + B, A - B };
}

사용 :

var results = SumAndSub(20, 5);
int sum = results[0];
int sub = results[1];

3
"프로그래머에게 시간과 잊을 수없는 방법이 필요하다"는 것은 무엇을 의미합니까?
토마스 플린 코우

2
결과 [0]를 두 번 사용했습니다. 문제의 증상입니다
symbiont

1
이것이 잊을 수없는 답변이라는 것에 의심의 여지가 없습니다.
Luis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.