두개를 많이 사용해보지 않았더라도 유니티를 사용해보셨다면 out한정자는 꽤 많이 보셨을것이다.
아마 레이캐스트를 사용할때의 매개변수로 맞은 오브젝트를 전달받아 무언가를 처리할때 가장 많이 보셨을것 같다.
하지만 주변에 있음에도 정확한 사용법과 차이를 모르고 넘어가는 경우가 있어서 정리 하려고 한다.
Swap 메소드 만들어보기(Call By Value)
public static void Swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
간단하게 2개의 정수값을 바꿔주는 메소드를 만들었다.
static void Main(string[] args)
{
int A = 10;
int B = 20;
Console.WriteLine($"A = {A}\nB = {B}");
Swap(A,B);
Console.WriteLine($"A = {A}\nB = {B}");
}
바뀌기 전 A,B 값을 출력하고, 스왑 메소드를 호출 후에 바뀐 값을 보기위해 다시 A,B 값을 출력했다.
Swap 메소드를 통해 값을 변경해줬음에도 불구하고 같은 값이 찍혔다.
매개변수도 근본적으로는 변수이기 때문에, 메소드 외부로부터 메소드 내부로 데이터를 전달받는 매개체 역할만을 한다.
Swap의 매개변수인 a와b는 A,B인수가 각각 대입되어 값이 복사가 된다(Call by Value)
a는 A의 값을 가지고, b는 B의 값을 가지지만 완전히 별개의 메모리 공간을 사용한다.
그래서 Swap함수가 호출되고 다시 A와B를 찍어도 두 변수의 값이 변하지 않았던 것이다.
Swap 메소드의 변경 (Call By Reference)
그렇다면 A와 B를 Swap함수로 변경시키려면 어떤 변화가 필요할까?
바로 ' ref ' 매개변수 한정자를 사용하는 것이다.
Swap 함수의 매개변수에 ref 한정자를 넣어주었다.
public static void Swap(ref int a,ref int b)
{
int temp = a;
a = b;
b = temp;
}
메인 함수에도 Swap 메소드를 호출할때 ref한정자로 A와 B를 넘겨주었다.
static void Main(string[] args)
{
int A = 10;
int B = 20;
Console.WriteLine($"A = {A}\nB = {B}");
Swap(ref A,ref B);
Console.WriteLine($"A = {A}\nB = {B}");
}
ref 한정자를 이용한 참조에 의한 전달은 원본 변수를 직접 참조한다.
A,B를 ref 한정자로 직접 넘겨주었기 때문에 2개의 값이 서로 바뀌어서 잘 나오는것을 알수있다.
메소드의 참조 반환
메소드의 반환값도 ref 한정자를 통해 참조로 넘겨줄 수 있다.
메소드의 반환 데이터 타입 앞에 ref 한정자를 붙여주고, 리턴도 ref 한정자를 붙여서 리턴해주면 된다.
실험을 위해 Salary라는 클래스와 mySalary, 그리고 Salary를 참조로 넘겨주는 메소드를 만들었다.
class Salary
{
public int mySalary = 100;
public ref int SearchSalary()
{
return ref mySalary;
}
}
메인 클래스에서 샐러리의 인스턴스를 생성하고,
변수 A에 아까 만들어둔 메소드로 값을 넘겨주었다.
A에 값을 더하기전 변수의 값을 출력하고,
A에 100이라는 값을 더해준 후에 변수의 값을 출력했다.
class MainApp
{
static void Main(string[] args)
{
Salary mySalary = new Salary();
int A = mySalary.SearchSalary();
Console.WriteLine($"A = {A}\n샐러리 ={mySalary.mySalary}");
A += 100;
Console.WriteLine($"변경후\nA = {A}\n샐러리 ={mySalary.mySalary}");
}
}
A를 참조로 전달 받고 A값을 수정했음에도 샐러리의 값이 바뀌지 않았다.
이유는 A변수에 ref 한정자를 쓰지 않았기 때문이다.
static void Main(string[] args)
{
Salary mySalary = new Salary();
ref int A = ref mySalary.SearchSalary(); //ref 한정자를 붙여줌
Console.WriteLine($"A = {A}\n샐러리 ={mySalary.mySalary}");
A += 100;
Console.WriteLine($"변경후\nA = {A}\n샐러리 ={mySalary.mySalary}");
}
변수 A앞에 ref 한정자를 붙이고, 메소드 앞에도 ref 한정자를 붙여주었다.
실행 해보니 잘 참조되어 샐러리의 값까지 변경된것을 볼 수 있다.
반대로 생각해보면 메소드를 만들때 ref 한정자를 붙여 만들고 리턴을 한다 하더라도 받는 변수에서 ref 한정자를 쓰지 않는다면 일반 메소드와 똑같이 쓸 수 있다는 뜻이 된다.(Call By Value)
2개의 결과를 요구하는 메소드
일반적으로 메소드의 결과는 하나면 충분하지만 가끔 두 개 이상의 결과를 요구하는 메소드들도 있다.
이럴때 ref 한정자를 이용해 결과를 저장 해줄수있다.
static void Main(string[] args)
{
int TotalCarrot = 300;
int A = 0; //몫
int B = 0; //나머지
Console.WriteLine($"{A}\n{B}");
CalCarrot(TotalCarrot, 7, ref A, ref B);
Console.WriteLine($"변경 후\n{A}\n{B}");
}
//당근을 나눠줄수있는 몫과 나머지를 계산하는 메소드
static void CalCarrot(int TotalCarrot, int num, ref int quotient, ref int remainder)
{
quotient = 300 / num; //몫
remainder = 300 % num; //나머지
}
300개의 당근을 매개변수 num 명에게 나눠준다면 몇명에게 나눠줄수있고, 남은 수량은 몇인지 계산하는 메소드를 만들엇다.
ref 한정자를 이용해 A,B에 각각 몫과 나머지를 넣어주는 메소드다.
메소드를 통해 7명에게 당근을 나눠줬을때의 몫과 나머지가 변수에 잘 들어갔다.
매개변수 한정자 Out 의 등장, ref와의 차이
Out 매개변수 한정자는 ref와는 달리 안전장치를 제공해준다.
어떤 안전장치냐면 out 한정자를 이용하여 매개변수를 넘길때 매개변수에 결과를 저장하지 않으면 컴파일러가 에러 메세지를 뱉어낸다.
아래 예시를 통해 확인해보자.
위의 코드에서 ref를 전부 out로 바꾼 코드를 작성했다.
static void Main(string[] args)
{
int TotalCarrot = 300;
int A = 0; //몫
int B = 0; //나머지
Console.WriteLine($"{A}\n{B}");
CalCarrot(TotalCarrot, 7, out A, out B);
Console.WriteLine($"변경 후\n{A}\n{B}");
}
static void CalCarrot(int TotalCarrot, int num, out int quotient, out int remainder)
{
quotient = 300 / num; //몫
remainder = 300 % num; //나머지
}
아래 움짤을 보면 Out으로 넘긴 매개변수 remainder에 값을 넣어준 코드를 주석처리 하면 '제어가 현재 메서드를 벗어나기 전에 'remainder' out 매개 변수를 할당해야 합니다.' 라는 에러메세지를 낸다.
즉, out 매개변수 한정자를 이용하여 매개변수를 넘기면 해당 매개변수에 무조건 결과를 저장해야한다.
반면에 ref 매개변수 한정자는 이런 안전장치가 없다.
위의 ref와 out 매개변수 한정자의 차이점을 보고나면 다른 차이점도 충분히 유추 해볼수있는데,
그것은 바로 초기화의 여부이다.
ref 매개변수 한정자는 변수를 초기화 하지 않은 상태로 인수로 넘긴다면 에러를 뱉어낸다.
반면에 out 매개변수 한정자를 사용하면 초기화를 하지 않더라도 에러가 뜨지 않는다.
왜 아무런 에러가 뜨지 않을까 잘 생각해보면 위에서 언급한 out의 안전장치 때문이다.
메소드 안에서 out으로 넘긴 매개변수에 결과를 넣지 않으면 에러를 내기 때문에, 굳이 초기화 시킨 변수가 아니더라도 어떠한 값이 들어간다는 확신이 있기 때문이다.
또, Out 매개변수 한정자를 사용하면 변수를 미리 선언해둘 필요 없이 메소드를 호출할 때 변수를 선언 할 수 있다.
ref는 변수 초기화가 필수이기 때문에 이것을 허용해주지 않는다.
C# 책을 읽고 공부한 내용을 정리해봤다.
ref와 out에 대해서 헷갈리는 부분이 많았는데 , 이번 정리를 통해 개념이 확실히 잡힌것같다.
이 글을 통해 도움이 되신 분이 한분이라도 있었으면 좋겠다.
'프로그래밍 공부 > C# 프로그래밍' 카테고리의 다른 글
[C#] 프로퍼티란? & 자동구현 프로퍼티와 public 변수와의 차이 찾아보기 (0) | 2022.08.24 |
---|---|
[C#] 메소드 오버로딩,가변 개수의 인수, 명명된 인수, this() 생성자 (0) | 2022.08.24 |
[Unity_C#] Switch문 & Switch 식 (0) | 2022.08.21 |
클래스와 구조체 / 배열과 리스트 / awake와 start의 차이 (0) | 2022.08.11 |
가비지 컬렉터(garbage collector) (0) | 2022.08.08 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!