안녕하세요. BlockDMask입니다.
오늘은 C#에서 매개변수를 넘길 때 붙여줄 수 있는 한정자 중에 ref라는 키워드에 대해서 알아보려고 합니다.
<목차>
1. ref 키워드 설명
2. ref 키워드 예제
3. ref, out, in 키워드 간단 요약
1. C# ref 키워드 설명
1-1) ref 가 하는 일
기본적으로 C# 함수(메서드)에 인자를 전달할 때 call by value (값 전달)을 합니다.
ref 키워드를 이용하면 명시적으로 call by refence(참조 전달)을 할 수 있습니다.
참조로 전달을 하면 무슨 일이 일어나는가 하면, 매개변수로 전달한 값의 원본이 변경이 되는 것입니다.
우선 값 전달을 먼저 이야기 해보면
값 타입 (int, float, struct... ) 들은 기본적으로 매개변수로 보낼 때 call by value 값이 전달되기 때문에
전달하는 변수와의 관계가 끊어지게 됩니다.
public void ChangeNum(int num)
{
num = 10;
}
int a = 99;
ChangeNum(a);
Console.WriteLine(a); // a 는 그대로 99 입니다.
이렇게 값이 99인 a를 우리가 전달을 하더라도 ChangeNum에서 num으로 매개변수를 받을 때 call by value (값 전달)이기 때문에 문자 그대로 99만 전달이 되는 것입니다.
그래서 함수 밖에 있는 a와 ChangeNum 내부에 있는 num은 아예 관계가 없는 것이라 생각하면 됩니다.
참조로 전달을 하면 해당 변수에 별칭이 생긴다고 생각하면 됩니다.
아래에서 설명하겠지만 ref 키워드를 명시적으로 붙였기 때문에 이제 call by reference가 됩니다.
public void ChangeNum(ref int num)
{
num = 10;
}
int a = 99;
ChangeNum(ref a);
Console.WriteLine(a); // a 는 10 입니다.
이렇게 되면 변수 a도, num도 동일한 변수를 나타내게 되고 함수 내부에서 값을 바꾸면
함수 밖에 있는 것에도 영향을 주게 됩니다.
글로 하니 설명하기 어려운데 위의 예제 2개를 아래 그림으로 보면 이해하기 쉬울 것입니다.
**심화
참조를 전달 한 다는 것은 참조 형식과 개념이 다릅니다.
값 형식이 참조로 전달될 때 boxing 되지 않습니다.
그대로 전달된 매개변수에 별칭을 붙여서 해당 변수의 값을 변경하는 방식입니다.
**참조 타입의 객체들 같은 경우에는 기본적으로 call by value로(ref 키워드를 붙이지 않고 전달) 전달하더라도
함수 내부에서 객체의 상태를 변경할 수 있습니다. (함수 밖에 있는 객체에 영향을 줌)
명시적으로 전달하거나 객체 내부가 아닌 객체 자체를 변경할 수도 있으므로 구분을 확실히 해서 ref 키워드를 붙여주시기 바랍니다.
1-2) ref 사용방법과 특징
- ref의 경우 변수를 전달하기 전에 초기화가 반드시 되어 있어야 합니다.
- ref는 함수의 매개변수 앞부분에서도 키워드를 작성해주어야 되며,
함수 호출 시에도 ref 키워드를 붙여 주어야 합니다.
// 함수 선언 시 매개변수 앞에 ref 붙여줌
public void ExFunc(ref int num) { ... };
// 보낼 변수 초기화 필수
int a = 10;
// 함수 호출 시 변수 앞에 ref 붙여줌
ExFunc(ref a);
2. C# ref 키워드 예제
2-1) 값 타입 ref 비교
이렇게 값 타입(int, float, struct...)의 경우에는 ref를 붙이고 안 붙이고의 차이가 확실합니다.
ref를 붙이지 않은 경우에는 함수 밖에 있는 변수에 영향을 전혀 주지 않고
ref를 붙이는 경우에 함수 밖에 있는 변수에 영향을 주게 됩니다.
2-2) 참조 타입 ref 비교
결과를 보면 참조 타입(class...)은 ref를 선언하든 안 하든 함수 인자로 넘겨서 값을 변경하게 되면
함수 밖에 있는 것에도 영향을 주는 것을 알 수 있습니다.
2-3) 참조 타입 객체 할당 ref 비교
잘 보면 새로운 객체를 생성해서 연결을 할 때, 이때 참조 타입에서 차이가 나게 됩니다.
이건 설명을 그림과 함께 진행해보겠습니다.
아래 그림은 ChangeVal 함수를 호출했을 때 그림입니다.
참조 타입을 call by value 할 때는 위의 캡처처럼 스택에 새롭게 주소 1을 가지고 있는 innerMyName 변수를 하나 더 만든다고 생각하면 됩니다. (포인터를 하나 더 만드는 느낌)
그 상태에서 새로운 객체를 할당하게 되면 innerMyName은 새로운 객체의 주소인 주소 2를 가리키게 됩니다.
원래 있던 myName은 그대로 주소 1을 가지고 있겠죠?
(스택에 따로 존재하되, 같은 주소를 가리키고 있었던 것뿐이었기 때문)
하지만, ChangeRef를 호출했을 때를 한번 볼까요?
위 그림을 보면 ref를 통해서 호출하게 되면, 변수의 별명이 된다고 말씀드렸잖아요.
그러니 스택에 있는 동일한 변수에 별명만 생긴 것입니다.
그림 왼쪽과 같이 말이죠. 이상태에서 myName, innerMyName 변수는 주소 1을 가리키다가
innerMyName = new MyName(222,...)를 통해 새 객체를 할당하게 되면 주소 2를 가리키게 됩니다.
당연히 innerMyName과 myName은 같은 변수이니 같은 주소 2를 가리키게 되겠죠?
이게 참조 타입에서 call by value와 call by reference의 차이입니다.
이해하기 쉽게 시간을 들여서 그림으로 그려봤는데, 제 노력이 여러분들의 이해에 도움이 되었으면 좋겠습니다.
이상으로 오늘은 C# ref 키워드에 대해서 알아보았습니다.
감사합니다.
'<개인공부> > [C#]' 카테고리의 다른 글
[C#] out 매개변수 한정자 설명 및 예제 (out 키워드) (4) | 2022.11.05 |
---|---|
[C#] in 매개변수 한정자 설명 및 예제 (in 키워드) (5) | 2022.10.29 |
[C#] 확장 메서드 (Extension Method) 설명과 예제 (0) | 2022.10.15 |
[C#] Equals, ==, ReferenceEquals 비교 및 설명 (0) | 2022.10.08 |
[C#] 실행 시간 측정, 코드 수행 시간 측정(Stopwatch) (0) | 2022.09.25 |
[C#] const, readonly 설명 사용법과 차이점에 대해서 (0) | 2022.09.22 |
[C#] 타입 조사 Type, 리플렉션(reflection) (0) | 2022.09.11 |
[C#] 인덱서 객체(인스턴스)를 배열 처럼 사용해보자 (0) | 2022.09.04 |