기본 콘텐츠로 건너뛰기

C# 제너릭(Generic) 타입과 제너릭 클래스


 


1. 제너릭(Generic) 개념 이해하기

1.1 제너릭이란?

  • **제너릭(Generic)**은 하나의 클래스나 메서드를 여러 자료형에 대해 재사용할 수 있도록 만드는 문법적 기능입니다.
  • 예를 들어, 정수 리스트, 문자열 리스트, 사용자 정의 타입 리스트 등을 각각 따로 만드는 대신 List<T> 한 가지로 다양한 타입을 지원할 수 있게 됩니다.

1.2 왜 제너릭을 사용할까?

  1. 재사용성: 같은 로직을 여러 타입에 대해 중복 작성할 필요가 없으므로 생산성이 높아집니다.
  2. 타입 안전성: 컴파일 시점에 타입이 결정되기 때문에, 박싱/언박싱이나 object 캐스팅 오류 등을 미연에 방지합니다.
  3. 성능: 박싱/언박싱이 제거되어 성능 손실이 줄어들고, 실행 중에 런타임 타입 체크 부담이 감소합니다.

2. 제너릭 타입의 예시

2.1 내장 제너릭 컬렉션

C#에서 가장 흔하게 접할 수 있는 제너릭 타입은 **컬렉션(Collection)**입니다.

  • List<T>, Dictionary<TKey, TValue>, Queue<T>, Stack<T> 등

예: List<T>를 이용해 정수 목록을 다루기

List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);

foreach (var num in numbers)
{
    Console.WriteLine(num); // 10, 20
}
  • List<int>는 ‘정수 전용 리스트’가 됩니다.
  • numbers에 문자열이나 다른 타입을 잘못 추가하면 컴파일 에러가 발생하므로 안전합니다.

2.2 Dictionary<TKey, TValue> 예시

Dictionary<string, int> scores = new Dictionary<string, int>();
scores["Alice"] = 90;
scores["Bob"] = 80;

Console.WriteLine(scores["Alice"]); // 90
  • scores는 키를 문자열(string)로, 값을 정수(int)로 지정했습니다.
  • 다른 타입을 섞어 넣으려 하면 컴파일 시점에 에러가 납니다.

3. 제너릭 클래스 직접 만들어보기

3.1 기본 제너릭 클래스 선언

public class MyGenericClass<T>
{
    private T _value;

    public MyGenericClass(T initialValue)
    {
        _value = initialValue;
    }

    public T GetValue()
    {
        return _value;
    }
}
  • MyGenericClass<T>: T는 타입 매개변수(Type Parameter)입니다.
  • 클래스 내부의 모든 곳에서 T라는 추상적인 타입을 사용할 수 있습니다.

사용 예시

// int형 버전
MyGenericClass<int> intBox = new MyGenericClass<int>(100);
Console.WriteLine(intBox.GetValue()); // 100

// string형 버전
MyGenericClass<string> stringBox = new MyGenericClass<string>("Hello");
Console.WriteLine(stringBox.GetValue()); // Hello

3.2 제너릭 클래스 다중 타입 매개변수

제너릭 클래스는 타입 매개변수를 여러 개 가질 수도 있습니다:

public class Pair<T1, T2>
{
    public T1 First { get; set; }
    public T2 Second { get; set; }

    public Pair(T1 first, T2 second)
    {
        First = first;
        Second = second;
    }
}

// 사용 예시
Pair<string, int> pair = new Pair<string, int>("Age", 25);
Console.WriteLine($"{pair.First} = {pair.Second}"); // "Age = 25"
  • Pair<T1, T2>는 첫 번째 타입과 두 번째 타입을 함께 묶는 구조를 제공합니다.
  • Pair<string, int> / Pair<int, double> / Pair<Foo, Bar> 등 다양한 타입 조합으로 쓸 수 있습니다.

4. 제너릭 메서드

클래스 자체가 아닌 메서드에만 제너릭을 적용할 수도 있습니다.

public static T GetMax<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) > 0 ? a : b;
}

public static void Main()
{
    int maxInt = GetMax(10, 20);      // GetMax<int>
    string maxString = GetMax("A", "B"); // GetMax<string>
    
    Console.WriteLine(maxInt);      // 20
    Console.WriteLine(maxString);   // B
}
  • GetMax<T> 메서드는 where T : IComparable<T>라는 **제한(Constraint)**을 통해 CompareTo 메서드가 있는 타입만 받도록 합니다.
  • 이렇게 제너릭 메서드를 사용하면 매개변수 타입만 다르고 로직이 같은 함수를 여러 번 오버로드할 필요가 없습니다.

5. 제너릭 제약(Constraints)

5.1 왜 제약이 필요할까?

  • 제너릭은 타입에 대한 구체적인 정보를 갖고 있지 않으므로, 제너릭 내부에서 ‘필요한 멤버(예: 메서드, 프로퍼티)’를 안전하게 호출할 수 있도록 제약을 지정합니다.

5.2 주요 제약 예시

  • where T : struct : T는 값 타입만 가능
  • where T : class : T는 참조 타입만 가능
  • where T : new() : T는 매개변수가 없는 기본 생성자를 가져야 함
  • where T : BaseClass : T는 특정 클래스(또는 추상 클래스)를 상속받아야 함
  • where T : InterfaceName : T는 특정 인터페이스를 구현해야 함

예시: new() 제약

public class Factory<T> where T : new()
{
    public T CreateInstance()
    {
        return new T(); // 기본 생성자가 있는 T만 가능
    }
}
  • where T : new()로 제약했기 때문에, T에 매개변수가 없는 기본 생성자가 반드시 존재해야 합니다.
  • 그렇지 않다면 컴파일 시점에 오류가 발생합니다.

6. 제너릭을 사용할 때 주의할 점

  1. 타입 안정성: 제너릭을 올바른 타입으로 선언해야 합니다. 예: List<int>에 int만 추가
  2. Boxing/Unboxing: 제너릭을 쓰면 일반 object 대신 구체적인 타입을 다루므로 성능 이점이 큽니다.
  3. 제약 사용: 제너릭 내부에서 특정 멤버를 사용해야 한다면 적절한 제약(Constraints)을 사용하세요.
  4. 코드 가독성: 제너릭이 과도하게 복잡해지면 오히려 가독성이 떨어질 수 있습니다. 적절히 추상화하는 것이 중요합니다.

7. 종합 예제

아래는 제너릭 클래스와 제너릭 메서드, 제약을 함께 사용하는 예시입니다.

// 특정 타입이 IComparable<T>를 구현해야만 동작하는 정렬 기능을 가진 컬렉션
public class SortableList<T> where T : IComparable<T>
{
    private List<T> _items = new List<T>();

    public void Add(T item)
    {
        _items.Add(item);
    }

    public void SortItems()
    {
        _items.Sort(); // T가 IComparable<T>여야 .Sort() 가능
    }

    public void PrintAll()
    {
        foreach (var item in _items)
        {
            Console.WriteLine(item);
        }
    }
}

// 사용 예시
public class Program
{
    public static void Main()
    {
        // int는 IComparable<int>를 구현하므로 사용 가능
        SortableList<int> intList = new SortableList<int>();
        intList.Add(30);
        intList.Add(10);
        intList.Add(20);

        intList.SortItems();
        intList.PrintAll(); // 10, 20, 30

        // string도 IComparable<string>을 구현
        SortableList<string> stringList = new SortableList<string>();
        stringList.Add("Banana");
        stringList.Add("Apple");
        stringList.Add("Cherry");

        stringList.SortItems();
        stringList.PrintAll(); // "Apple", "Banana", "Cherry"
    }
}

마무리

  • C#의 제너릭(Generic) 문법은 타입의 안전성과 재사용성을 극대화해주는 중요한 기법입니다.
  • 컬렉션, 메서드, 클래스 등 다양한 범위에서 제너릭을 적용할 수 있으며, 제약(Constraints)을 통해 구체적인 메서드 호출, 생성자 호출 등을 제어할 수 있습니다.
  • 실제 현업 코드(예: 데이터 처리, 알고리즘 라이브러리)에서는 제너릭을 적절히 활용해 효율적이고 유지보수 쉬운 구조를 만들 수 있습니다.

Key Points

  1. 재사용: 하나의 제너릭 클래스/메서드로 여러 타입 지원
  2. 안전성: 컴파일러가 타입 불일치를 잡아냄
  3. 성능: 불필요한 Boxing/Unboxing 제거
  4. 제약(Constraints): 필요한 멤버나 생성자 호출을 위해 특정 조건을 부여

제너릭을 익숙해지면 코드 품질이 한 단계 올라갈 것이니, 꼭 직접 작성해보고 테스트해보세요!

감사합니다.

댓글

이 블로그의 인기 게시물

Log4Net vs. Serilog 비교

🔍 Log4Net vs. Serilog 간단 비교 기준Log4NetSerilog 성능 중간 (충분하지만, 최신 라이브러리보단 느림) 빠르고 효율적 설정 방식 XML 기반 (전통적) JSON 기반 (모던함) 구조적 로깅 미지원 (기본 텍스트 로그) 강력한 구조적 로깅 지원 ASP.NET Core 통합 가능하지만 설정이 좀 복잡 간단하고 직관적 Sink(대상) 다양성 적당한 편 매우 다양하고 확장성 높음 생태계 & 유지보수 전통적, 유지보수 상태는 다소 정체 활발한 개발과 업데이트 🎯 어떤 프레임워크가 좋을까? Serilog를 추천하는 이유: 최신 기술 : ASP.NET Core와 완벽히 통합, 구조적 로깅이 뛰어나 JSON 로그 처리, 분석에 최적화됨. 높은 생산성 : 설정과 유지보수가 쉬움. 코드 기반 및 JSON 설정으로 직관적이고 빠른 개발이 가능. 확장성 : 파일, 콘솔, DB, Elasticsearch, Seq 등 다양한 Sink를 제공해 향후 확장성이 좋음. 🚀 결론 요약 Log4Net을 추천할 때Serilog를 추천할 때 기존 시스템과 호환이 필수 최신 ASP.NET Core 프로젝트 XML 설정 선호 JSON, 코드 기반 설정 선호 구조적 로깅 필요 없음 구조적 로깅과 분석 필요

키움증권 Open API를 활용하여 갭 매매 전략 구현- C#

  1. 갭 매매 전략이란? 갭 매매란 주가가 전일 종가 대비 큰 폭으로 상승(갭업)하거나 하락(갭다운)하여 개장할 때, 발생한 갭의 움직임을 활용하여 수익을 내는 전략입니다. 이번에 소개할 전략은 다음 조건에 해당하는 종목을 대상으로 합니다. 조건검색 : 전일 대비 오늘 시가가 5% 이상 갭업으로 시작한 종목 전략 실행 : 개장 후 30분 이내에 주가가 전일 종가 수준까지 회귀하면 매수 진입 후, 갭이 완전히 메워질 때 매도합니다. 손절매 : 진입 가격에서 갭 상승분의 50% 하락 시 손절 익절매 : 갭 상승분의 50% 이상 추가 상승 시 익절합니다. 2. 구현 환경 및 준비사항 키움증권 API+ 설치 (영웅문 API) Visual Studio Community Edition (C#) 키움증권 계좌 (모의투자 계좌 추천) 3. C# 코드 전체 구현 (주석 포함) 다음 코드에는 갭 매매 전략, 주문 실행, 체결 확인 및 정정 주문까지 포함되어 있습니다. using System; using System.Collections.Generic; using AxKHOpenAPILib; public class GapTrading { private AxKHOpenAPI axKHOpenAPI; private string accountNumber; // 매수 후 체결된 정보를 저장할 Dictionary (종목코드, (체결가격, 체결수량)) private Dictionary< string , ( int price, int qty)> positions = new Dictionary< string , ( int , int )>(); public GapTradingStrategy (AxKHOpenAPI api, string accountNo) { axKHOpenAPI = api; accountNumber = accountNo; ...

실시간 웹 애플리케이션 구축하기: SignalR 이용하기

  IT 개발자로서 실시간 통신 기능을 웹 애플리케이션에 통합하는 것은 사용자 경험을 대폭 향상시킬 수 있는 중요한 방법 중 하나입니다. 오늘은 Microsoft의 SignalR을 이용하여 실시간 웹 애플리케이션을 구축하는 방법을 소개하려 합니다. SignalR은 웹소켓을 사용하여 실시간 통신을 쉽게 구현할 수 있도록 도와주는 라이브러리입니다. SignalR 소개 SignalR은 .NET 개발자들이 실시간 웹 기능을 손쉽게 구현할 수 있도록 지원하는 라이브러리입니다. 클라이언트와 서버 간의 양방향 통신을 가능하게 해 주며, 채팅 애플리케이션, 실시간 게임, 실시간 데이터 업데이트 등 다양한 실시간 애플리케이션을 개발할 때 유용하게 사용됩니다. 주요 기능 자동 연결 관리 : SignalR은 연결, 재연결 및 연결 해제 과정을 자동으로 처리합니다. 규모 확장성 : SignalR은 Redis, Azure SignalR Service 등을 통해 서버를 확장할 수 있는 기능을 지원합니다. 다양한 플랫폼 지원 : JavaScript, .NET, Java 등 다양한 클라이언트에서 사용 가능합니다. 실시간 채팅 애플리케이션 구축하기 개발 환경 설정 .NET Core SDK 설치 : SignalR은 .NET Core에 포함되어 있으므로, .NET Core SDK가 설치되어 있어야 합니다. Visual Studio 또는 VS Code 설치 : 개발 환경으로 Visual Studio 또는 VS Code를 사용할 수 있습니다. 프로젝트 생성 Visual Studio에서 새 프로젝트 생성 : 'ASP.NET Core Web Application' 선택 프로젝트 이름과 위치 설정 'ASP.NET Core Empty' 템플릿 선택 SignalR 라이브러리 추가 : NuGet 패키지 관리자를 통해 Microsoft.AspNetCore.SignalR을 설치합니다. SignalR 허브 생성 SignalR 허브는 클라이언트와 서버 간의 통신을 중개하는 역할을 합니다...

.NET MAUI: 크로스플랫폼 앱 개발의 미래

  1. .NET MAUI란? **.NET MAUI (Multi-platform App UI)**는 마이크로소프트에서 개발한 크로스플랫폼 애플리케이션 프레임워크로, 하나의 코드베이스로  iOS, Android, Windows, macOS 에서 실행되는 애플리케이션을 만들 수 있습니다. 주요 특징 단일 코드베이스 : 하나의 코드로 여러 플랫폼 지원 네이티브 UI : 각 플랫폼의 네이티브 UI 컨트롤을 활용 .NET 기반 : C#과 .NET 기술을 사용 MVU 및 MVVM 패턴 지원 핫 리로드 지원 : UI 변경 시 즉시 반영 강력한 성능 및 최적화 2. .NET MAUI 개발 환경 설정 필수 도구 운영체제 필수 소프트웨어 Windows Visual Studio 2022, .NET SDK 6 이상 Mac Visual Studio for Mac, .NET SDK 6 이상, Xcode 프로젝트 생성 방법 Visual Studio에서 생성 Visual Studio 2022 실행 "새 프로젝트 만들기" 선택 .NET MAUI 앱 템플릿 선택 프로젝트 이름 및 경로 설정 후 생성 명령어로 생성 dotnet new maui -n MyMauiApp cd MyMauiApp dotnet build dotnet run 3. .NET MAUI 프로젝트 구조 📂 MyMauiApp ├── 📂 Platforms # Android, iOS, macOS, Windows 코드 ├── 📂 Resources # 이미지, 폰트, 스타일 등 ├── 📂 Views # XAML UI 코드 ├── 📂 Models # 데이터 모델 ├── 📂 ViewModels # MVVM 패턴을 위한 ViewModel ├── App.xaml # 전역 스타일 및 테마 ├── MainPage.xaml # 메인 페이지 UI ├── MauiProgram.cs # 앱 초기 설정 4. 기본 UI 개발 (XAML & C#) XAM...

스마트팜과 AI

  스마트팜과 AI 융합, 농업 혁신의 미래를 보다 안녕하세요, IT 개발자 여러분 그리고 기술에 관심이 많은 일반 독자분들! 오늘은 농업과 최신 IT 기술의 만남, 바로 "스마트팜과 AI"에 대해서 이야기하고자 합니다. 스마트팜 기술은 이미 다양한 분야에서 활용되고 있지만, 인공지능(AI)을 접목하면 어떤 가능성이 있는지, 또 어떤 방향으로 개발하면 좋을지 함께 생각해보겠습니다. 스마트팜이란? 스마트팜은 사물인터넷(IoT) 기술을 농업에 적용하여 온도, 습도, 일조량, 영양분 등을 자동으로 제어하는 시스템입니다. 이미 많은 농가에서 사용하고 있는 기술로, 효율성을 높이고 노동력을 줄이는 데 크게 기여하고 있습니다. 왜 스마트팜에 AI가 필요한가? 기존 스마트팜은 센서를 통해 데이터를 수집하고 이를 기반으로 단순 제어만 수행했습니다. 하지만 AI가 결합되면 수집된 데이터를 심층적으로 분석하여 보다 정밀한 제어와 예측이 가능해집니다. 예를 들어, AI를 활용한 이미지 인식 기술을 통해 병충해 발생을 초기에 발견하고 대응할 수 있으며, 빅데이터 분석을 통한 작물 생장 예측으로 생산량과 품질을 획기적으로 향상시킬 수 있습니다. AI 스마트팜 아이디어 제안 1. 작물 건강 모니터링 시스템 이미지 처리 딥러닝 모델을 이용해 식물 잎이나 줄기의 이미지를 실시간 분석하여 병충해 및 영양 결핍 상태를 자동으로 감지하고 경고 메시지를 제공합니다. 2. 자율 농장 로봇 AI 기반의 자율 주행 로봇을 개발하여 농작물의 관리(파종, 관수, 수확)를 자동화합니다. 강화학습(Reinforcement Learning)을 적용해 로봇이 스스로 환경에 적응하고 효율적인 작업 방식을 학습하도록 합니다. 3. 예측형 기후 대응 시스템 과거 기상 데이터와 현재 환경 데이터를 머신러닝으로 분석하여 작물 생육에 최적화된 환경 조건을 예측하고 제어합니다. 갑작스러운 기후 변화에도 농작물 피해를 최소화할 수 있습니다. 4. 스마트 해충 방제 시스템 AI 기반으로 해충의 종류와 개체수를...