티스토리 뷰
깊은복사, 얕은복사 (값에 의한 복사, 참조에 의한 복사)
class Program { static void Main(string[] args) { Deep d1 = new Deep(); //struct 값형식 d1.a = 10; Deep d2 = d1; d2.a = 20; Console.WriteLine("d1.a의값 : {0}, d2.a의값 : {1}", d1.a, d2.a); //struct구조체는 값형식의 특성을 띄고 있습니다. //struct변수는 할당받은 값과 같은 값을 가지고 //복사가 이루어져 새로운 인스턴스를 생성한 것 같은 특징을 띄고 있습니다. //결과적으로 d2가 가지고 있는 a변수의 변경에 d1은 영향을 받지 않습니다. //이런것을 '깊은복사'라고 합니다. Shallow s1 = new Shallow(); //class 참조형식 s1.a = 10; Shallow s2 = s1; s2.a = 20; Console.WriteLine("s1.a의값 : {0}, s2.a의값 : {1}", s1.a, s2.a); //class는 참조형식입니다. //class의 변수는 값으로 주소값을 갖습니다. //class변수는 값의 대입시 인스턴스의 주소값만을 가리키게 됩니다. //결과적으로 s2의 변경이 s1에도 영향을 주게 됩니다. 서로 같은 인스턴스를 바라보고 있기 때문입니다. //(새로운 인스턴스를 생성하는 키워드 new를 사용할 때는 제외) //이런것을 '얕은복사'라고 합니다. } } class Shallow { public int a; } struct Deep { public int a; }
ref, out 예약어
- 메서드를 호출하는 문장의 인자에 들어가는 값을 참조형식으로 사용할 수 있도록 지원해주는 예약어
- 기본형 타입의 변수가 메서드의 매개변수에 들어가면 값에 의한 복사가 일어납니다.
반대로 참조형 변수가 매개변수에 들어가면 참조 복사(참조 주소를 변수에 할당 및 복사)가 일어납니다.
전자를 call by value라고 하고, 후자를 call by reference라고 합니다.
ref하고 out예약어는 call by value를 call by reference처럼 사용할 수 있도록 하는 예약어입니다.
ref의 특징
- 반드시 선언한 메서드의 매개변수와 이를 호출하는 구문의 인자에 ref예약어를 선언 해야한다.
- ref 변수는 변수 초기화(할당)가 되어 있어야 한다.
out의 특징
- out 변수는 값을 할당하지 않아도 되며, 할당한다 해도 그 값이 적용되지 않는다.
- 메서드 구현부 안에서 반드시 out변수의 값을 할당한 후 return이 되야한다.
class Program { static void Main(string[] args) { int callBy = 10; CallByVal(callBy); Console.WriteLine(callBy);
//값에 의한 복사가 일어나기 때문에 callByVal에서 변수 val의 변경이 변수callBy에 영향을 주지 않는다.
//callBy = 10 CallByRef(ref callBy); Console.WriteLine(callBy);
//callBy가 값형식의 변수로 기존에는 스택 값의 복사가 일어나지만 ref예약어의 사용으로 인해
//callBy변수가 가리키는 데이터의 주소값이 전달되어 메서드의 지역변수 val의 변경이 callBy에도 영향을 미친다.
//callBy = 11 int callBy2; //out으로 넘길 변수는 초기화를 강제하지 않는다. CallByOut(out callBy2); //callBy2 = 11 } static void CallByVal(int val) { //val은 값에 의한 복사 이기때문에 callBy의 값만이 복사된다. //지역변수인 val의 변경이 callBy에는 영향이 없다. val = val + 1; } static void CallByRef(ref int val) { //ref 예약어를 이용해서 val을 사용하기 때문에 val은 callBy의 주소를 참조한다. //고로 지역변수인 val의 변경이 callBy변수에도 영향을 미친다. val = val + 1; } static void CallByOut(out int val) { //out 매개 변수는 메서드 안에서 반드시 값을 지정해줘야 된다. val = 10; val++; } }
그렇다면 class에 대해 ref 예약어를 적용한다면 어떻게 될까?
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RefOutEnumApplication { class Test{ public int result = 10; public override String ToString() { return result; } } class Program { static void Main(string[] args) { Test t = new Test(); UpdateTest(t); Console.WriteLine("ref를 사용한 변수t -------> "+t); ㅜㅜ //결과 100
Test t1 = new Test(); UpdateTest1(t1); Console.WriteLine("ref를 사용하지 않은 변수t1 -------> " + t1);ㄴ //결과 10
ㄴ Test t2 = new Test(); UpdateTest2(ref t2); Console.WriteLine("ref를 사용한 변수t2 -------> " + t2);
//결과 100 } static void UpdateTest(Test tt) { tt.result = 100;
//얕은 복사로 인스턴스의 주소가 복사되어 변수 tt에 할당 되기 때문에
//이 구문을 만나면 Main메서드의 변수 t도 영향을 받아 result가 변경된다. } static void UpdateTest1(Test tt) { tt = new Test(); tt.result = 100;
//tt 변수가 Main메서드의 t1변수의 인스턴스를 가리키고 있지만
//위의 new Test()구문을 만나 새로운 인스턴스를 지역변수 tt에 할당하고 있다.
//이 때는 tt와 t1변수의 주소가 다르기 때문에 tt의 변경이 t1에 영향을 주지 못한다. } static void UpdateTest2(ref Test tt) { tt = new Test(); tt.result = 100; //ref예약어를 사용할 때는 다른 양상을 보인다.
//참조형타입을 대상으로
//ref를 사용하지 않으면 얕은 복사가 일어나는데 얕은 복사는 주소의 복사로 인해 할당되는 것이고
//ref를 사용할 때는 스택의 주소값을 직접 전달하는 것이기 때문에
//해당 구문안에서의 새로운 new 할당이 Main메서드의 변수 t2에도 영향을 끼친다. } } }
'C#' 카테고리의 다른 글
상속(inherit) (0) | 2016.01.16 |
---|---|
?? 키워드와 goto문 (0) | 2015.05.29 |
[BaseClassLibrary]직렬화/역직렬화 (0) | 2014.12.05 |
C# 예외처리 (0) | 2014.11.19 |
CLR(C#가상머신) 초기화시 값을 전달할 경우 app.config 활용 (0) | 2014.11.18 |