아… 조금 다릅니다. 기본적으로 예라고 말씀드릴 수 있고, 세부적인 동작은 아니라고 말씀드릴 수 있습니다.
“Hello World” 형태를 문자열 리터럴이라 하는데요, 힙 메모리에 위치하지 않습니다. 이 문자열은 특별하게 컴파일 되었을 때 실행파일의 리소스가 되며, 실행 파일이 실행될 때 같이 코드와 함께 메모리로 적재되며, 실행이 종료될 때까지 그 메모리 주소 위치가 변하지 않습니다.
using System.Runtime.InteropServices;
var a = "Hello, world!";
PrintPtr("a", ref a);
var b = a;
PrintPtr("b", ref b);
var c = "Hello, world!";
PrintPtr("c", ref c);
void PrintPtr<T>(string name, ref T obj)
{
var objHandle = GCHandle.Alloc(obj, GCHandleType.Pinned);
var address = objHandle.AddrOfPinnedObject();
Console.WriteLine($"{name} : {address}");
}
결과
a : 2408203814924
b : 2408203814924
c : 2408203814924
a와 b의 메모리 주소가 같은 것은 이해됩니다. var b = a;를 통해 같은 위치를 바라보게 되었기 때문입니다. 그런데 c까지도 메모리 주소가 같네요?
C# string는 문자 리터럴을 통해 string을 생성할 경우, 동일한 문자 리터럴일 경우 그 개체를 공유합니다.
C# 에서 메서드에 parameter를 넘기는 동작은 ref, out 키워드를 사용하지 않는 한 기본적으루 모두 복사 후 새변수에 할당입니다.
다만 그 대상이 argument의 타입에 따라
값형식일 경우 값복사 후 새 변수에 할당
참조형식일 경우 참조복사 후 새 변수에 할당
이지요.
void Main()
{
var a = new A(1);
Console.WriteLine($"a hash :{a.GetHashCode()}");
var aa = Set100(a);
Console.WriteLine($"Set100 aa hash :{aa.GetHashCode()}");
Console.WriteLine($"a:{a.Number}");
Console.WriteLine($"aa:{aa.Number}");
}
private A Set100(A aa) // 요기서 a 의 참조를 복사해 aa 변수에 할당.
{
// 밖에서 전달한 a 와 파라미터 aa 는 같은 참조를 지닌 다른 변수입니다.
Console.WriteLine($"aa hash :{aa.GetHashCode()}");
aa = new A(100); // 이건 밖에서 전달한 a 가 아닌 aa 변수에 새 객체 할당.
return aa;
}
class A
{
public int Number { get; set; }
public A(int number) => this.Number = number;
}
C# 을 그냥 책으루 공부하다보믄 메서드 인자 전달 방식에 대한 설명이
보통 그냥 값형식은 값복사, 참조형식은 참조전달… 이라고만 되어 있어서 이런 혼란이 오는 거 같아요.
제 생각에… 요걸 혼란스럽지 않게 표현한다면
메서드에 인자 전달은 ref, out 키워드 가 없을 때는 그냥 무조건 복사해서 새 변수에 담는다.
다만 인자의 타입에 따라 복사 대상이 달라질 뿐이다.