Search

이것이 C#이다: 5장 - 코드의 흐름 제어하기

서론

안녕하세요, 컴퓨터 공학을 전공하고 게임 개발에 관심 있는 취업 준비생입니다. 오늘은 '이것이 C#이다' 책의 5장 내용을 정리하며, C#에서 코드의 흐름을 제어하는 방법에 대해 깊이 있게 살펴보겠습니다. 특히 switch 문, 점프문, 그리고 C#의 강력한 기능인 패턴 매칭에 초점을 맞춰 설명하겠습니다.

본론

1. 조건문: switch 문과 패턴 매칭

C#의 switch 문은 단순히 값을 비교하는 것을 넘어서 강력한 패턴 매칭 기능을 제공합니다. 이는 코드의 가독성을 높이고 복잡한 조건 로직을 간결하게 표현할 수 있게 해줍니다.

1.1 케이스 가드

케이스 가드(case guard)는 switch 문의 case 절에 추가적인 조건을 부여하는 기능입니다. when 키워드를 사용하여 구현합니다.
switch (obj) { case int i: Console.WriteLine($"It's an integer: {i}"); break; case float f when f >= 0: Console.WriteLine($"It's a non-negative float: {f}"); break; case float f: Console.WriteLine($"It's a negative float: {f}"); break; default: Console.WriteLine("It's neither an integer nor a float"); break; }
C#
복사
이 예제에서 float 타입의 경우, 양수와 음수를 구분하여 처리하고 있습니다. 이러한 방식은 복잡한 조건문을 보다 구조화된 형태로 표현할 수 있게 해줍니다.

1.2 switch 식

C# 8.0부터는 switch 식(expression)을 사용할 수 있습니다. 이는 기존의 switch 문을 더욱 간결하게 만들어줍니다.
string GetGrade(int score, bool repeated) => score switch { 90 when repeated == true => "B+", 90 => "A", 80 => "B", 70 => "C", 60 => "D", _ => "F" };
C#
복사
switch 식은 함수형 프로그래밍 스타일을 선호하는 개발자들에게 특히 유용합니다. 코드가 더 간결해지고, 가독성이 향상되며, 실수할 가능성도 줄어듭니다.

2. 점프문: goto의 재발견

C#은 break, continue, return, throw와 같은 일반적인 점프문 외에도 goto 문을 제공합니다. goto는 흔히 안티패턴으로 여겨지지만, 특정 상황에서는 유용할 수 있습니다.
for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 3; k++) { if (someCondition) goto EXIT_NESTED_LOOPS; Console.WriteLine($"i:{i}, j:{j}, k:{k}"); } } } EXIT_NESTED_LOOPS: Console.WriteLine("Exited nested loops");
C#
복사
이 예제에서 goto는 깊게 중첩된 루프를 한 번에 빠져나오는 데 사용됩니다. 하지만 goto의 과도한 사용은 코드의 흐름을 복잡하게 만들 수 있으므로 주의가 필요합니다. 대부분의 경우 함수 추출이나 다른 제어 구조를 사용하는 것이 더 좋은 방법입니다.

3. 패턴 매칭: C#의 강력한 기능

패턴 매칭은 C# 7.0부터 도입된 강력한 기능으로, 복잡한 데이터 구조를 효과적으로 분석하고 처리할 수 있게 해줍니다. 이는 함수형 프로그래밍 언어에서 영감을 받아 설계되었으며, 객체 지향 패러다임과 함수형 프로그래밍 스타일을 융합한 좋은 예시입니다.
패턴 매칭을 사용하면 코드의 가독성이 향상되고, 복잡한 조건문을 더 간결하고 명확하게 표현할 수 있습니다. 또한, 컴파일러가 패턴 매칭의 완전성을 검사하므로 런타임 오류를 줄일 수 있습니다.
이제 C#에서 제공하는 다양한 패턴 유형에 대해 자세히 살펴보겠습니다.

3.1 선언 패턴 (Declaration Pattern)

선언 패턴은 입력 데이터의 타입을 검사하고, 해당 타입으로 캐스팅까지 동시에 수행합니다.
object obj = "Hello, World!"; if (obj is string str) { Console.WriteLine($"String length: {str.Length}"); }
C#
복사
이 패턴은 타입 검사와 캐스팅을 한 번에 처리하여 코드를 간결하게 만듭니다.

3.2 형식 패턴 (Type Pattern)

형식 패턴은 C# 9.0에서 도입되었으며, 선언 패턴과 유사하지만 변수 선언 없이 타입만 확인합니다.
object obj = 42; if (obj is int) { Console.WriteLine("It's an integer"); }
C#
복사

3.3 상수 패턴 (Constant Pattern)

상수 패턴은 입력 값이 특정 상수와 일치하는지 검사합니다.
object obj = 3.14; if (obj is 3.14) { Console.WriteLine("It's pi!"); }
C#
복사

3.4 관계 패턴 (Relational Pattern)

관계 패턴은 C# 9.0에서 도입되었으며, 숫자형 데이터의 대소 관계를 비교합니다.
static string GetTemperature(double celsius) => celsius switch { > 30 => "Hot", > 20 => "Warm", > 10 => "Cool", _ => "Cold" };
C#
복사

3.5 논리 패턴 (Logical Pattern)

논리 패턴은 여러 패턴을 and, or, not 연산자로 결합합니다.
static string ClassifyNumber(int n) => n switch { < 0 and > -10 => "Small negative", < 0 => "Negative", 0 => "Zero", > 0 and < 10 => "Small positive", > 0 => "Positive", };
C#
복사

3.6 프로퍼티 패턴 (Property Pattern)

프로퍼티 패턴은 객체의 속성을 검사합니다.
class Product { public string Name { get; set; } public decimal Price { get; set; } } static string ClassifyProduct(Product p) => p switch { { Name: "Apple", Price: < 1.0m } => "Cheap apple", { Name: "Apple", Price: >= 1.0m } => "Expensive apple", { Name: "Orange" } => "Orange", _ => "Unknown product" };
C#
복사

3.7 위치 패턴 (Positional Pattern)

위치 패턴은 튜플이나 분해 가능한 타입의 요소를 패턴 매칭합니다.
static string Classify(object obj) => obj switch { (1, string id) => $"ID: {id}", (2, int age, string name) => $"Name: {name}, Age: {age}", (3, _, _) => "Three elements, first is 3", _ => "Unknown pattern" };
C#
복사

3.8 var 패턴 (var Pattern)

var 패턴은 모든 입력과 매칭되며, 해당 값을 변수에 할당합니다.
static void ProcessValue(object obj) { if (obj is var x) { Console.WriteLine($"Type: {x.GetType()}, Value: {x}"); } }
C#
복사

3.9 무시 패턴 (Discard Pattern)

무시 패턴은 언더스코어(_)를 사용하여 특정 값을 무시합니다.
static string GetFirstElement(object obj) => obj switch { (var first, _) => $"First: {first}", _ => "Not a tuple" };
C#
복사

3.10 목록 패턴 (List Pattern)

C# 11에서 도입된 목록 패턴은 배열이나 리스트의 요소를 패턴 매칭합니다.
static string AnalyzeList(int[] numbers) => numbers switch { [1, 2, ..] => "Starts with 1, 2", [.., 3, 4] => "Ends with 3, 4", [1, .., 4] => "Starts with 1, ends with 4", [] => "Empty list", _ => "Does not match any pattern" };
C#
복사

3.11 패턴 매칭의 이점 및 주의점

패턴 매칭의 주요 이점은 다음과 같습니다:
1.
코드 가독성 향상
2.
복잡한 조건문의 간결한 표현
3.
컴파일 시점에서의 패턴 완전성 검사
4.
데이터 구조의 분해와 분석 용이성
하지만 과도한 사용은 오히려 코드를 복잡하게 만들 수 있으므로 적절한 사용이 중요합니다. 또한, 성능에 민감한 상황에서는 패턴 매칭의 오버헤드를 고려해야 합니다.

결론

C#의 흐름 제어 구문, 특히 패턴 매칭은 코드의 가독성과 유지보수성을 크게 향상시킵니다. switch 식과 다양한 패턴을 활용하면, 복잡한 로직을 더 간결하고 명확하게 표현할 수 있습니다.
이번 학습을 통해 C#의 현대적인 기능들이 얼마나 강력한지 다시 한 번 깨달았습니다. 특히 패턴 매칭은 함수형 프로그래밍의 장점을 객체 지향 언어에 도입한 훌륭한 예시라고 생각합니다.

참고 자료

박상현, "이것이 C#이다", 한빛미디어, 2023