서론
C#에서 메소드는 코드의 재사용성과 가독성을 높이는 핵심 요소입니다. 이번 포스트에서는 '이것이 C#이다' 책의 6장을 바탕으로, C# 메소드의 고급 기능들을 살펴보겠습니다. 이러한 기능들은 코드를 더욱 효율적이고 유연하게 만들어주며, 특히 복잡한 로직을 다룰 때 큰 도움이 됩니다.
본론
1. 참조에 의한 매개변수 전달
C#에서는 ref 키워드를 사용하여 매개변수를 참조로 전달할 수 있습니다. 이는 메소드 내에서 매개변수의 값을 변경하고, 그 변경사항을 호출자에게 반영하고자 할 때 유용합니다.
static void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"x: {x}, y: {y}"); // 출력: x: 2, y: 1
C#
복사
이 기능은 값을 교환하거나, 큰 객체를 복사하지 않고 전달할 때 유용합니다. 하지만 과도한 사용은 코드의 복잡성을 증가시킬 수 있으므로 주의가 필요합니다.
2. 메소드의 결과를 참조로 반환하기
C# 7.0부터는 메소드의 반환값을 참조로 다룰 수 있게 되었습니다. 이는 ref 반환값을 통해 구현됩니다.
public ref int GetLargestElement(int[] numbers)
{
int indexOfLargest = 0;
for (int i = 1; i < numbers.Length; i++)
{
if (numbers[i] > numbers[indexOfLargest])
{
indexOfLargest = i;
}
}
return ref numbers[indexOfLargest];
}
int[] numbers = { 1, 2, 3, 4, 5 };
ref int largest = ref GetLargestElement(numbers);
largest = 10; // numbers 배열의 값이 직접 변경됩니다.
Console.WriteLine(string.Join(", ", numbers)); // 출력: 1, 2, 3, 4, 10
C#
복사
이 기능은 큰 데이터 구조에서 특정 요소를 반환하고 수정해야 할 때 유용합니다. 하지만 참조 반환은 신중하게 사용해야 하며, 반환된 참조의 수명 주기를 고려해야 합니다.
3. 출력 전용 매개변수
out 키워드를 사용하면 메소드에서 여러 값을 반환할 수 있습니다. 이는 특히 메소드가 여러 결과를 생성하는 경우에 유용합니다.
static void DivideWithRemainder(int dividend, int divisor, out int quotient, out int remainder)
{
quotient = dividend / divisor;
remainder = dividend % divisor;
}
int q, r;
DivideWithRemainder(10, 3, out q, out r);
Console.WriteLine($"Quotient: {q}, Remainder: {r}"); // 출력: Quotient: 3, Remainder: 1
C#
복사
out 매개변수는 ref와 달리 메소드 내에서 반드시 값을 할당해야 합니다. 이는 API 설계 시 명확성을 제공합니다.
4. 가변 개수의 인수
params 키워드를 사용하면 메소드가 가변 개수의 인수를 받을 수 있습니다. 이는 유연한 API 설계에 매우 유용합니다.
static int Sum(params int[] numbers)
{
int total = 0;
foreach (int num in numbers)
{
total += num;
}
return total;
}
Console.WriteLine(Sum(1, 2, 3)); // 출력: 6
Console.WriteLine(Sum(10, 20, 30, 40)); // 출력: 100
C#
복사
이 기능은 메소드 오버로딩을 줄이고 코드를 더 간결하게 만들 수 있습니다.
5. 명명된 인수
명명된 인수를 사용하면 메소드 호출 시 매개변수의 이름을 명시적으로 지정할 수 있습니다. 이는 코드의 가독성을 크게 향상시킵니다.
static void CreateUser(string name, int age, string email)
{
// 사용자 생성 로직
}
CreateUser(name: "John", age: 30, email: "john@example.com");
C#
복사
이 기능은 특히 많은 매개변수를 가진 메소드를 호출할 때 유용합니다.
6. 선택적 인수
선택적 인수를 사용하면 메소드 호출 시 일부 인수를 생략할 수 있습니다. 이는 기본값을 가진 매개변수를 정의함으로써 구현됩니다.
static void PrintInfo(string name, int age = 30, string country = "Unknown")
{
Console.WriteLine($"Name: {name}, Age: {age}, Country: {country}");
}
PrintInfo("Alice"); // 출력: Name: Alice, Age: 30, Country: Unknown
PrintInfo("Bob", 25); // 출력: Name: Bob, Age: 25, Country: Unknown
PrintInfo("Charlie", 35, "USA"); // 출력: Name: Charlie, Age: 35, Country: USA
C#
복사
선택적 인수는 메소드의 다양한 사용 시나리오를 단일 메소드로 처리할 수 있게 해줍니다.
7. 로컬 함수
C# 7.0부터 도입된 로컬 함수는 메소드 내부에 정의되는 함수입니다. 이는 코드의 캡슐화와 가독성을 향상시킵니다.
static int Fibonacci(int n)
{
return Fib(n);
int Fib(int number)
{
if (number <= 2)
return 1;
return Fib(number - 1) + Fib(number - 2);
}
}
Console.WriteLine(Fibonacci(6)); // 출력: 8
C#
복사
로컬 함수는 외부 함수의 변수에 접근할 수 있어, 클로저와 유사한 기능을 제공합니다.
결론
C# 메소드의 고급 기능들을 탐구하면서, 이 언어의 설계 철학과 강력함을 새롭게 인식하게 되었습니다. ref와 out 키워드는 C#이 메모리 관리와 성능 최적화를 어떻게 균형 있게 다루는지 보여주는 좋은 예시입니다. 이들은 다른 언어의 포인터나 참조 개념을 안전하고 효율적으로 구현한 것으로, C#의 type-safe한 특성을 유지하면서도 필요할 때 저수준의 제어를 가능케 합니다.
로컬 함수의 도입은 C#이 함수형 프로그래밍의 개념을 어떻게 절차적/객체지향적 패러다임과 조화롭게 통합하는지 보여줍니다. 이는 코드의 캡슐화와 재사용성을 높이는 동시에, 클로저와 유사한 기능을 제공함으로써 더 표현력 있는 코드 작성을 가능하게 합니다.
참고 자료
•
박상현, "이것이 C#이다", 한빛미디어, 2023