CodeGym /행동 /C# SELF /함수에서 여러 값 반환하기: out-파라미터와 튜플

함수에서 여러 값 반환하기: out-파라미터와 튜플

C# SELF
레벨 11 , 레슨 3
사용 가능

1. 들어가기

상상해봐, 우리가 어떤 함수가 있는데, 사용자(예를 들어 이름으로)를 받아서 나이, 가입일, 활성화 플래그까지 한 번에 다 반환해야 해. C랑 예전 C# 버전에서는 반환 타입이 하나만 되는 게 당연했어. 근데 반환해야 할 값이 여러 개면 어쩌지? 이 질문 때문에 여러 가지 방식이 생겼고, 각각 장단점이 있어.
이전 강의에서 이미 튜플에 대해 살짝 봤었지. 이제 out 파라미터를 배워보고, 이 두 가지 방법을 비교해볼 거야.

가장 많이 쓰는 방법들:

  • out 파라미터를 사용한다.
  • 필요한 필드를 가진 어떤 객체(익명 타입이나 직접 만든 클래스)를 반환한다.
  • 튜플(ValueTuple)을 반환한다.

이번 강의에서는 이 중에서 out 파라미터랑 튜플, 이 두 가지를 집중적으로 다루고, "정면승부"로 비교해서 함수에서 여러 값을 반환할 때 각각의 장점을 파악해볼 거야.

2. out 파라미터: 과거에서 온 인사

이런 함수 보면:


void GetUserInfo(string userName, out int age, out DateTime registrationDate, out bool isActive)
{
    // 여기서 계산
    age = 42;
    registrationDate = new DateTime(2010, 1, 1);
    isActive = true;
}

이게 함수야, 아니면 무슨 세차장 지점이야? 반환하는 게 너무 많아서, 전부 메인 반환값이 아닌데 밖으로 나가네!

어떻게 동작하는지

호출하는 쪽에서 미리 변수를 선언해놔야 하고, 함수가 그걸 채워줘:

int age;
DateTime reg;
bool isActive;

GetUserInfo("Bob", out age, out reg, out isActive);

// 이제 모든 변수에 값이 들어감
Console.WriteLine($"{age}, {reg}, {isActive}");

out 방식의 단점

  • 읽기 어렵다: 메서드 시그니처가 옆으로 길어지고, 반환되는 데이터의 의미가 파라미터 이름만 보고는 잘 안 보일 때도 있어.
  • 체이닝 불편: 이런 메서드는 체이닝(예를 들어 결과를 바로 다른 메서드에 넘기기)이 힘들어.
  • 넘긴 변수를 바꾼다: 메서드가 기존 변수를 꼭 수정해야 하고, out을 빼먹으면 컴파일 에러가 나.
  • 초기화 지연: 결과를 한 번만 쓰고 싶어도, 외부 변수를 미리 선언해야 해서 귀찮아.
  • 큰 메서드에서 가독성 떨어짐: out 파라미터가 많으면 뭐가 뭔지 헷갈리기 쉬워.

여기서 튜플이 빛을 발하기 시작하지!

3. 튜플 — 진화된 방식

예제로 비교해보자

튜플로 하면:


public (int Age, DateTime RegistrationDate, bool IsActive) GetUserInfo(string userName)
{
    // DB에서 찾는 척
    return (42, new DateTime(2010, 1, 1), true);
}

사용법:

// 한 번에 모든 값을 받고, 변수 이름도 지정
var (age, regDate, isActive) = GetUserInfo("Bob");
Console.WriteLine($"{age}, {regDate}, {isActive}");

쓸데없는 선언도 없고, out도 없고, 완전 읽기 쉽지!

비교표: out vs tuple

기준 Out-파라미터 튜플(ValueTuple)
시그니처 길고, out-파라미터가 리스트에 있음 간결하고, 전부 한 "묶음"으로 반환
사용법 변수 선언하고, 그 다음에 메서드 호출 바로 디컨스트럭트 가능
가독성 out-파라미터가 많으면 자주 헷갈림 요소 이름이 바로 보여서 이해 쉬움
조합 쉬움? 아니 응, 중첩도 되고 옮기기도 쉬움

튜플 VS. out — 내부적으로 어떻게 동작할까

out 파라미터는 사실 함수가 자기 "영역" 밖의 메모리랑 일하는 거야: 쉽게 말하면, 다른 데서 만들어진 변수를 바꿔버리는 거지. 그래서 조심해야 해, 실수로 변수 상태를 엉망으로 만들 수도 있으니까(컴파일러가 꼭 할당하라고 강제해줘서 다행!).

튜플은 함수가 데이터를 구조체로 만들어서, 완전히 초기화한 다음에 한 번에 반환하는 거야. 그러니까 전부 한 "묶음"으로, 잃어버릴 일도 없고 깜빡할 일도 없어.

코드 읽기와 유지보수

공감하지? 일주일 뒤에 남의 코드 볼 때, 이런 게 보이면:

(var age, var city, var isActive) = GetUserInfo("Anna");

이게 훨씬 낫지, 이런 것보다:

int age;
string city;
bool isActive;
GetUserInfo("Anna", out age, out city, out isActive);

튜플 덕분에 메서드 시그니처가 덜 복잡해지고, 결과도 직관적으로 이해할 수 있어.

4. 튜플이 특히 좋은 상황

1. 결과랑 에러 메시지를 같이 반환해야 할 때

public (bool Success, string ErrorMessage) TryProcess(string data)
{
    if (string.IsNullOrEmpty(data))
        return (false, "데이터 없음");

    // 데이터 처리...
    return (true, "");
}

var (ok, error) = TryProcess(input);
if (!ok)
    Console.WriteLine($"오류: {error}");

2. 여러 검색 결과를 반환하는 함수

public (User? FoundUser, int Index) FindUserByName(User[] users, string name)
{
    for (int i = 0; i < users.Length; i++)
    {
        if (users[i].Name == name)
            return (users[i], i);
    }
    return (null, -1);
}

3. "쌍" 값: 최소값, 최대값 계산

public (int min, int max) FindMinMax(int[] numbers)
{
    int min = numbers[0], max = numbers[0];
    foreach (var n in numbers)
    {
        if (n < min) min = n;
        if (n > max) max = n;
    }
    return (min, max);
}

var (minValue, maxValue) = FindMinMax(arr);
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION