ref, out, in - C# 파라미터 키워드 제대로 구분하자
개발하다 보면 한 번쯤은 이런 코드를 마주친다.
public void DoSomething(ref int value) { ... }
public void DoSomething(out int result) { ... }
public void DoSomething(in int input) { ... }
처음 보면 다 비슷하게 생겼다. '결국 값을 전달하는 건데 ref, out, in 이게 도대체 뭐가 다른 건데?' 라는 생각이 들 수밖에 없다.
심지어 StackOverflow나 구글에서 검색해도 다들 "ref는 이렇고 out은 저렇고..." 이론적인 설명만 한가득이다. 그런데 그걸 그대로 복붙하듯 외워봤자 실무에선 잘 써지지 않는다.
이번 글에선 실제 상황을 기반으로 세 가지 키워드를 확실하게 정리해보자.
문제 상황: "값을 바꾸고 싶다는데 왜 안 바뀌는 거지?"
public void ChangeValue(int number)
{
number = 100;
}
int x = 5;
ChangeValue(x);
Console.WriteLine(x); // ???
출력은 100일까? 아니다. 5가 출력된다.
왜냐면 C#은 값 타입(value type)을 함수로 넘기면 "복사본"이 전달되기 때문이다. 함수 안에서 아무리 바꿔도 원본은 그대로다.
그래서 나온 게 바로 ref, out, in이다.
ref - 값도 주고, 결과도 받고 싶을 때
public void Double(ref int num)
{
num = num * 2;
}
int x = 10;
Double(ref x);
Console.WriteLine(x); // 20
ref
는 원본 값을 넘기고, 함수 안에서 그 값을 바꿔서 되돌려줄 수 있다.
즉, "얘를 네가 좀 바꿔줘" 라는 느낌이다.
- 주의: 함수 호출 전에 반드시 초기화되어 있어야 한다.
실무에서는 이런 경우에 쓴다:
- 여러 값을 리턴하고 싶은데, return은 하나밖에 못할 때
- 성능상 값 복사가 부담스러울 때 (큰 구조체 등)
out - 값을 받아오고 싶을 때 (반드시!)
public void TryParse(string s, out int result)
{
result = int.Parse(s);
}
int number;
TryParse("123", out number);
Console.WriteLine(number); // 123
out
은 "내가 뭔가를 계산해서 값을 넣어줄게. 너는 빈 손으로 와도 돼"라는 의미다.
- 주의: 함수 안에서 반드시 값을 설정해야 한다.
out은 특히 Try-패턴에서 자주 쓰인다.
예를 들어 C#의 int.TryParse
, DateTime.TryParse
이런 메서드들이 대표적이다.
in - 참조는 주지만, 건들지 마
public void PrintLength(in string text)
{
Console.WriteLine(text.Length);
}
string message = "Hello World";
PrintLength(in message);
in
은 참조는 넘기되, 함수 안에서 읽기만 가능하다. 수정은 안 됨.
- 주의: C# 7.2부터 지원됨
in은 언제 쓰나?
- 읽기 전용으로 참조를 넘기고 싶을 때
- 성능 최적화를 위해 큰 구조체를 읽기 전용으로 넘길 때
실무 예시: 다중 결과 처리
public bool TryGetUser(string id, out User user)
{
if (db.Contains(id)) {
user = db.Get(id);
return true;
}
user = null;
return false;
}
User foundUser;
if (TryGetUser("hong", out foundUser))
{
Console.WriteLine(foundUser.Name);
}
이럴 때 out이 없다면 어떻게 해야 할까?
- User 객체를 null로 리턴하고
- 리턴값을 따로 받고
- 상태 체크도 따로 해야 하고...
→ out은 리턴값과 별도로 데이터를 전달하는 깔끔한 방법이다.
언제 어떤 걸 써야 할까?
구분 | ref | out | in |
---|---|---|---|
의미 | 값 넘기고 바꿔서 받기 | 초기화 안 해도 됨, 바꿔서 받기 | 읽기 전용으로 넘기기 |
초기화 필요? | 필요함 | 필요 없음 | 필요함 |
함수 내 설정 | 해도 되고 안 해도 됨 | 반드시 해야 함 | 못 함 |
용도 | 양방향 데이터 전달 | 다중 값 리턴 | 읽기 전용 최적화 |
마무리하며
ref
, out
, in
은 전부 "참조로 넘긴다"는 공통점이 있지만, 의도와 사용법은 완전히 다르다.
- "값을 받아오고 싶다" → out
- "값을 넘기고 바꾸고 싶다" → ref
- "읽기만 하고 싶다" → in
이렇게 정리해두면, 앞으로 코드를 볼 때도 작성할 때도 기준이 생긴다.
무조건 ref부터 쓰는 습관은 버리고, 그 상황에서 어떤 목적이 있는지를 먼저 따져보자. 그게 실력자의 차이다.
'C#' 카테고리의 다른 글
object vs dynamic vs var – 언제 뭘 써야 헷갈리지 않을까? (3) | 2025.06.06 |
---|---|
값 타입 vs 참조 타입 – 구조체와 클래스, 뭐가 다른데? (22) | 2025.06.05 |
인터페이스 vs 추상 클래스, 진짜 차이가 뭔데? (26) | 2025.06.03 |
C# 기초부터 고급까지 Chapter 3.10. .NET Core로 REST API 설계 – 보안, 인증 포함 (2) | 2025.05.14 |
C# 기초부터 고급까지 Chapter 3.9. CQRS + MediatR 패턴 실전 예제 (0) | 2025.05.13 |