프로그래밍을 배우다 보면 call by value와 call by reference라는 개념을 한 번쯤은 접해봤을 것이다. 간단히 설명하면, call by value는 변수의 값을 복사해서 전달하는 것이고, call by reference는 메모리상의 주소와 같이 변수에 직접 접근할 수 있는 정보를 전달하는 것이다. 예를 들어, A가 어떠한 문서를 필요로 하는 상황에서 해당 문서를 복사해서 복사본을 전달하는 것은 call by value이고, 해당 문서의 원본 자체를 전달하는 것은 call by reference이다.
기본적으로 int, double과 같은 원시 자료형 (primitive data type)으로 선언된 매개변수에 대해서 C#은 call by value 방식을 통해 매개변수를 전달한다. 그러나 프로그래밍을 하다 보면, swap() 메소드처럼 변수 자체의 값을 변경해야 하는 경우도 있다. 즉, call by reference 방식으로 매개변수를 전달해야 하는 경우가 있다. C#에서는 call by reference 방식으로 매개변수를 전달해야 하는 경우를 다루기 위해 ref와 out 키워드를 제공한다.
1-1) ref 키워드
Call by reference 방식으로 매개변수를 전달하고자 할 때, ref 키워드를 이용하면 된다. 키워드에서 쉽게 유추할 수 있듯이 ref 키워드는 reference를 의미한다. 사용 방식은 아래의 [코드 1]과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | namespace ex { class Program { static void Main(string[] args) { int num1 = 5; int num2 = 10; Swap(ref num1, ref num2); Console.WriteLine("num1: {0} \t num2: {1}", num1, num2); } static void Swap(ref int arg1, ref int arg2) { int temp = arg1; arg1 = arg2; arg2 = temp; } } } | cs |
[코드 1] ref 키워드의 이용
실행 결과:
num1: 10 num2: 5
주의해야될 사항은 ref 키워드로 전달되는 변수는 반드시 초기화가 되어있어야 한다는 것이다. 만약 [코드 1]의 7~8행에서 변수를 초기화해주지 않고 10행의 Swap() 메소드를 호출하면, 컴파일러는 에러를 출력한다.
1-2) out 키워드
위의 ref 키워드는 매개변수를 전달하기 전에 반드시 초기화를 해야 한다는 제약이 있었다. 그러나 아래의 [코드 2]와 같이 어떠한 메소드에서는 메소드의 내부에서 매개변수에 값을 할당하는 경우도 있다. 이러한 경우에 매개변수를 반드시 초기화해서 전달해야 한다는 ref 키워드의 제약은 불필요한 동작이 추가적으로 실행되는 현상을 초래한다.
이러한 문제점을 해결하기 위해 out 키워드는 초기화되지 않은 변수를 call by reference 방식으로 전달할 수 있도록 허용한다. 아래의 [코드 2]에서는 out 키워드를 이용하여 call by reference 방식으로 Func() 메소드에 매개변수를 전달하고 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | namespace ex { class Program { static void Main(string[] args) { int num1; int num2; Func(out num1, out num2); } static void Func(out int arg1, out int arg2) { arg1 = 5; arg2 = 10; // ... } } } | cs |
[코드 2] out 키워드의 이용
[코드 2]의 메인을 보면, num1과 num2를 초기화하지 않고 Func() 메소드에 전달하는 것을 볼 수 있다. 만약 ref 키워드를 이용하였다면, 컴파일 에러가 발생하겠지만, out 키워드를 이용하면 컴파일 에러가 발생하지 않는다. 그러나 out 키워드를 이용할 시에는 메소드 내부에 15~16행과 같이 매개변수에 값을 할당하는 구문이 반드시 존재해야 한다.
확장 메소드를 이용하면 기존 클래스의 기능을 확장할 수 있다. 정확히는 기존 클래스에 메소드를 추가한다. 아래의 [코드 3]은 확장 메소드의 선언 형식을 나타낸다.
1 2 3 4 5 6 | static class 확장클래스식별자 { public static 반환형식 메소드식별자(this 확장하려는클래스 식별자, ... 매개변수들) { } } | cs |
[코드 3] 확장 메소드 선언 형식
아래의 [코드 4]는 확장 메소드의 구체적인 선언 및 이용 방식을 나타낸다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | namespace ex { class Program { static void Main(string[] args) { int x = 5; Console.WriteLine(x.Reverse()); } } static class Extension { public static double Reverse(this int me) { return 1 / (double)me; } } } | cs |
[코드 4] 확장 메소드의 선언 및 이용
위의 [코드 4]와 같이 확장 메소드는 반드시 정적 클래스 내부에 정적 메소드로 선언되어야 한다. [코드 4]의 15번째 줄에서는 Reverse()라는 메소드가 double 형식의 값을 반환하는 int 형식의 확장 메소드라고 선언하고 있다. 실제로 Visual Studio에서는 [코드 4]와 같이 확장 메소드를 선언하고, int 형식 변수와 .을 입력하면 멤버 메소드로 Reverse()가 나타나는 것을 볼 수 있다 [그림 1].
[그림 1] 확장 메소드 참조
확장 메소드는 많은 경우에 매우 유용하게 이용될 수 있다. 그러나 확장 메소드를 남용하면 코드가 번잡해지고, 클래스를 정의하는 의미가 사라지기 때문에 반드시 필요한 상황에만 이용하는 것이 좋다.
'C#' 카테고리의 다른 글
[C#] - Microsoft Text Analytics API (0) | 2016.10.09 |
---|---|
[C#] - Microsoft Emotion API (4) | 2016.10.05 |
[C#] - Visual Studio에서 Metro UI Framework 추가하기 (4) | 2016.10.03 |
[C#] - 클래스 정의와 상속 (0) | 2016.09.15 |