들어가며
C#에서는 클래스가 참조 타입이기 때문에 매개 변수로 넘길 때도 힙 메모리를 가리키는 포인터를(주소값) 넘기는 것으로 알고있다.
그러면 ref 키워드를 안써도 원본의 값을 변경할 수 있는데 왜 ref 키워드를 쓰는거지? 궁금해서 찾아보다가 정리하게 되었다.
클래스의 멤버 변수는 바꿀 수 있다.
using System.Collections.Specialized;
using System.Linq.Expressions;
namespace Test
{
class Program
{
class Point
{
private int x;
private int y;
public Point(int _x, int _y)
{
x = _x;
y = _y;
}
public void AddPoint(Point pos) // 이 부분이 핵심 코드
{
x = x + pos.x;
y = y + pos.y;
}
public void Print()
{
System.Console.WriteLine(x + ", " + y);
}
}
static void Main(string[] args)
{
Point p1 = new Point(1, 2);
Point p2 = new Point(10, 11);
p1.AddPoint(p2);
p1.Print();
p2.Print();
}
}
}
만약 이러한 코드가 있다고 할 때,
핵심 코드라고 적힌 부분의 AddPoint는 매개변수로 Point 클래스. 즉, 레퍼런스 타입을 인자로 받고 있다.

잘 출력한다.
public void AddPoint(Point pos)
{
x = x + pos.x;
y = y + pos.y;
pos.x = 100;
pos.y = 200;
}
그럼 해당 메서드에서 멤버 변수의 값을 바꿀 수 있는지 확인하기 위해 위와 같이 코드를 변경해보겠다.

변경되어 잘 출력한다.
그렇다면 왜 레퍼런스 타입을 인자로 넘길 때 ref 키워드를 사용하는걸까?
ref 키워드가 없다면 새로 할당한 레퍼런스 타입을 매개변수에 할당하지 못한다.
public void AddPoint(Point pos)
{
x = x + pos.x;
y = y + pos.y;
Point tmp = new Point(50, 60);
pos = tmp;
}
위와 같이 AddPoint 함수를 변경했다고 해보자.
p2의 출력값은 예상대로 50, 60이 될까?

그렇지 않다.
인자로 넘어온 클래스의 내부 멤버 변수 값은 변경이 가능하나,
함수 내에서 새로운 레퍼런스 객체를 할당해 대입하는 것은 불가능하다.
인자로 넘어온 pos는 참조 타입이라 포인터 느낌처럼 사용하는 건데, 해당하는 코드에선 가리키는 주소 자체를 바꾸려고 했기 때문이라 생각한다. (확실하지 않아서 태클 걸어주시면 감사합니다)
public void AddPoint(ref Point pos)
{
x = x + pos.x;
y = y + pos.y;
Point tmp = new Point(50, 60);
pos = tmp;
}
그래서 매개변수에 ref 키워드를 붙여주면 의도대로 변경된다.
(그럼 원래의 p2는 가비지가 되는건가?...)
p1.AddPoint(ref p2);
main문에서 위의 코드처럼 변경도 해주어야 한다.
ref 키워드는 원본의 값이 변경될 수 있다고(할 거라고) 알려주는 것이기 때문에, 사용하는 쪽에서도 ref 키워드를 써야하나 보다.

ref 키워드를 사용하니 의도대로 출력된다.
정리
레퍼런스 타입을 매개변수로 넘겨줄 때,
ref 키워드를 쓰지 않아도 내부 멤버 변수의 값은 변경이 가능하지만,
가리키는 주소값 자체를 바꾸는 것은 허용이 되지 않기 때문에 메서드 내에서 새로운 객체를 할당한 뒤, 인자에 넣어줄 거라면 ref 키워드를 써주는 것이 맞다.
P.S
그러면 매개변수로 넘기는 클래스의 멤버 변수의 값이 의도치 않게 바뀌는 것을 막으려면 어떻게 해야할까..
C++에서는 const 매개변수를 사용하는 것으로 알고있는데, 관련해서 C#에서는 못찾겠다.
알게 된다면 추가해야지
출처
https://www.csharpstudy.com/Mistake/Article/5
들어가며
C#에서는 클래스가 참조 타입이기 때문에 매개 변수로 넘길 때도 힙 메모리를 가리키는 포인터를(주소값) 넘기는 것으로 알고있다.
그러면 ref 키워드를 안써도 원본의 값을 변경할 수 있는데 왜 ref 키워드를 쓰는거지? 궁금해서 찾아보다가 정리하게 되었다.
클래스의 멤버 변수는 바꿀 수 있다.
using System.Collections.Specialized;
using System.Linq.Expressions;
namespace Test
{
class Program
{
class Point
{
private int x;
private int y;
public Point(int _x, int _y)
{
x = _x;
y = _y;
}
public void AddPoint(Point pos) // 이 부분이 핵심 코드
{
x = x + pos.x;
y = y + pos.y;
}
public void Print()
{
System.Console.WriteLine(x + ", " + y);
}
}
static void Main(string[] args)
{
Point p1 = new Point(1, 2);
Point p2 = new Point(10, 11);
p1.AddPoint(p2);
p1.Print();
p2.Print();
}
}
}
만약 이러한 코드가 있다고 할 때,
핵심 코드라고 적힌 부분의 AddPoint는 매개변수로 Point 클래스. 즉, 레퍼런스 타입을 인자로 받고 있다.

잘 출력한다.
public void AddPoint(Point pos)
{
x = x + pos.x;
y = y + pos.y;
pos.x = 100;
pos.y = 200;
}
그럼 해당 메서드에서 멤버 변수의 값을 바꿀 수 있는지 확인하기 위해 위와 같이 코드를 변경해보겠다.

변경되어 잘 출력한다.
그렇다면 왜 레퍼런스 타입을 인자로 넘길 때 ref 키워드를 사용하는걸까?
ref 키워드가 없다면 새로 할당한 레퍼런스 타입을 매개변수에 할당하지 못한다.
public void AddPoint(Point pos)
{
x = x + pos.x;
y = y + pos.y;
Point tmp = new Point(50, 60);
pos = tmp;
}
위와 같이 AddPoint 함수를 변경했다고 해보자.
p2의 출력값은 예상대로 50, 60이 될까?

그렇지 않다.
인자로 넘어온 클래스의 내부 멤버 변수 값은 변경이 가능하나,
함수 내에서 새로운 레퍼런스 객체를 할당해 대입하는 것은 불가능하다.
인자로 넘어온 pos는 참조 타입이라 포인터 느낌처럼 사용하는 건데, 해당하는 코드에선 가리키는 주소 자체를 바꾸려고 했기 때문이라 생각한다. (확실하지 않아서 태클 걸어주시면 감사합니다)
public void AddPoint(ref Point pos)
{
x = x + pos.x;
y = y + pos.y;
Point tmp = new Point(50, 60);
pos = tmp;
}
그래서 매개변수에 ref 키워드를 붙여주면 의도대로 변경된다.
(그럼 원래의 p2는 가비지가 되는건가?...)
p1.AddPoint(ref p2);
main문에서 위의 코드처럼 변경도 해주어야 한다.
ref 키워드는 원본의 값이 변경될 수 있다고(할 거라고) 알려주는 것이기 때문에, 사용하는 쪽에서도 ref 키워드를 써야하나 보다.

ref 키워드를 사용하니 의도대로 출력된다.
정리
레퍼런스 타입을 매개변수로 넘겨줄 때,
ref 키워드를 쓰지 않아도 내부 멤버 변수의 값은 변경이 가능하지만,
가리키는 주소값 자체를 바꾸는 것은 허용이 되지 않기 때문에 메서드 내에서 새로운 객체를 할당한 뒤, 인자에 넣어줄 거라면 ref 키워드를 써주는 것이 맞다.
P.S
그러면 매개변수로 넘기는 클래스의 멤버 변수의 값이 의도치 않게 바뀌는 것을 막으려면 어떻게 해야할까..
C++에서는 const 매개변수를 사용하는 것으로 알고있는데, 관련해서 C#에서는 못찾겠다.
알게 된다면 추가해야지
출처
https://www.csharpstudy.com/Mistake/Article/5