제네릭 소개 (Introduction to Generics)
제네릭(Generics)은 C#에서 타입의 재사용성을 높이고, 타입 안정성을 확보하며, 코드의 중복을 줄이는 강력한 기능입니다. 제네릭을 사용하면 클래스, 메서드, 인터페이스 등을 정의할 때 특정 데이터 타입에 구애받지 않고, 여러 타입을 사용할 수 있습니다. 이는 코드의 유연성을 높이고, 타입 안전성을 보장하여 런타임 오류를 줄이는 데 도움이 됩니다.
제네릭의 기본 개념 (Basic Concepts of Generics)
제네릭은 일반적인 데이터 타입을 사용하여 클래스, 메서드 또는 인터페이스를 정의할 수 있는 기능을 제공합니다. 제네릭을 사용하면 다음과 같은 이점을 얻을 수 있습니다.
- 타입 안정성: 컴파일 타임에 타입 검사가 이루어져 타입 오류를 줄일 수 있습니다.
- 코드 재사용성: 하나의 제네릭 클래스를 정의하고 여러 타입으로 인스턴스를 생성하여 재사용할 수 있습니다.
- 성능 향상: 제네릭을 사용하면 타입 변환 및 캐스팅이 필요 없어 성능을 향상시킬 수 있습니다.
제네릭 클래스 (Generic Classes)
제네릭 클래스를 사용하면 다양한 데이터 타입에 대해 유연하게 작동하는 클래스를 정의할 수 있습니다.
예제: 제네릭 클래스 정의 (Defining a Generic Class)
public class Box<T> { private T _content; public void SetContent(T content) { _content = content; } public T GetContent() { return _content; } }
이 예제는 Box<T>
라는 제네릭 클래스를 정의합니다. T
는 타입 매개변수로, 인스턴스를 생성할 때 구체적인 타입을 지정합니다.
예제: 제네릭 클래스 사용 (Using a Generic Class)
public class Program { public static void Main(string[] args) { Box<int> intBox = new Box<int>(); intBox.SetContent(123); Console.WriteLine($"Integer value: {intBox.GetContent()}"); Box<string> strBox = new Box<string>(); strBox.SetContent("Hello, Generics!"); Console.WriteLine($"String value: {strBox.GetContent()}"); } }
이 예제는 Box<int>
와 Box<string>
타입의 인스턴스를 생성하고, 각각의 SetContent
와 GetContent
메서드를 호출하여 제네릭 클래스를 사용하는 방법을 보여줍니다.
제네릭 메서드 (Generic Methods)
제네릭 메서드는 메서드의 매개변수 또는 반환 타입에 대해 제네릭을 사용하는 방법입니다. 제네릭 메서드는 호출 시점에 타입을 지정할 수 있습니다.
예제: 제네릭 메서드 정의 (Defining a Generic Method)
public class Utility { public static T FindMax<T>(T a, T b) where T : IComparable<T> { return a.CompareTo(b) > 0 ? a : b; } }
이 예제는 제네릭 메서드 FindMax<T>
를 정의합니다. 이 메서드는 두 값을 비교하여 더 큰 값을 반환합니다. T
는 IComparable<T>
를 구현해야 합니다.
예제: 제네릭 메서드 사용 (Using a Generic Method)
public class Program { public static void Main(string[] args) { int maxInt = Utility.FindMax(10, 20); Console.WriteLine($"Maximum integer: {maxInt}"); string maxStr = Utility.FindMax("apple", "banana"); Console.WriteLine($"Maximum string: {maxStr}"); } }
이 예제는 FindMax
메서드를 호출하여 정수와 문자열의 최대 값을 찾는 방법을 보여줍니다.
제네릭 인터페이스 (Generic Interfaces)
제네릭 인터페이스는 타입 매개변수를 사용하는 인터페이스입니다. 제네릭 인터페이스를 사용하면 타입에 대한 제약을 두고 인터페이스를 정의할 수 있습니다.
예제: 제네릭 인터페이스 정의 (Defining a Generic Interface)
public interface IRepository<T> { void Add(T item); T Get(int id); IEnumerable<T> GetAll(); }
이 예제는 제네릭 인터페이스 IRepository<T>
를 정의합니다. 이 인터페이스는 데이터 항목을 추가하고, 조회하며, 모든 항목을 반환하는 메서드를 포함합니다.
예제: 제네릭 인터페이스 구현 (Implementing a Generic Interface)
public class InMemoryRepository<T> : IRepository<T> { private readonly Dictionary<int, T> _storage = new Dictionary<int, T>(); private int _nextId = 1; public void Add(T item) { _storage[_nextId++] = item; } public T Get(int id) { _storage.TryGetValue(id, out T item); return item; } public IEnumerable<T> GetAll() { return _storage.Values; } }
이 예제는 IRepository<T>
인터페이스를 구현한 InMemoryRepository<T>
클래스를 정의합니다. 이 클래스는 메모리에 데이터를 저장하고, 항목을 추가하고 조회하는 방법을 제공합니다.
제네릭 타입 제약 (Generic Type Constraints)
제네릭 타입 제약을 사용하면 제네릭 타입 매개변수에 대해 제약 조건을 설정할 수 있습니다. 이를 통해 제네릭 클래스나 메서드가 특정 조건을 만족하는 타입만을 사용할 수 있도록 제한할 수 있습니다.
예제: 제네릭 타입 제약 (Using Generic Type Constraints)
public class DataProcessor<T> where T : class, new() { public T CreateInstance() { return new T(); } }
이 예제는 T
가 클래스이며 기본 생성자를 가져야 한다는 제약 조건을 설정합니다. new()
제약 조건을 사용하여 타입 T
의 인스턴스를 생성할 수 있습니다.
결론 (Conclusion)
제네릭(Generics)은 C#에서 타입 안정성과 코드 재사용성을 높이는 강력한 기능입니다. 제네릭 클래스를 사용하면 여러 타입에 대해 유연한 코드를 작성할 수 있으며, 제네릭 메서드는 메서드의 매개변수나 반환 타입에 제네릭을 사용하여 재사용성을 높입니다. 제네릭 인터페이스와 제약 조건을 사용하면 타입의 제약을 설정하고 더 안전하고 유연한 코드를 작성할 수 있습니다. 제네릭을 통해 코드의 중복을 줄이고, 컴파일 타임에 타입 검사를 수행함으로써 더 안전하고 유지보수 가능한 애플리케이션을 개발할 수 있습니다.