English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

C# 기본형

일반화는 특정 형식이 아니라 일반 형식을 의미합니다. C#에서 일반화는 특정 데이터 타입에 특정하지 않다는 것을 의미합니다.

C#은 타입 파라미터를 사용하여 특정 데이터 타입 없이 일반화된 클래스, 인터페이스, 추상 클래스, 필드, 메서드, 스태틱 메서드, 프로퍼티, 이벤트, 대리자 및 연산자를 정의할 수 있습니다. 타입 파라미터는 일반화된 타입의 인스턴스를 생성할 때 특정 타입으로 지정되는 대체자입니다.

타입 이름 뒤의 괄호에서 타입 파라미터를 지정하여 일반화를 선언합니다. 예를 들어, TypeName<T>에서 T는 타입 파라미터입니다.

일반화된 클래스

일반화된 클래스는 클래스 이름 뒤의 괄호에서 타입 파라미터를 사용하여 정의됩니다. 아래는 일반화된 클래스를 정의한 예입니다.

class DataStore<T>
{
    public T Data { get; set; }
}

위에서 DataStore는 일반화된 클래스입니다. T는 타입 파라미터로, DataStore 클래스의 필드, 프로퍼티, 메서드 파라미터, 반환 타입 및 DataStore 클래스의 대리자의 타입으로 사용됩니다. 예를 들어, Data는 타입 파라미터 T를 사용하여 특정 데이터 타입이 아닌 타입으로 정의된 일반화된 프로퍼티입니다.

주의: 일반적으로 하나의 타입 파라미터가 있을 때는 T를 사용할 수 있습니다. 여러 파라미터 타입이 있을 때는 T를 타입 파라미터로 사용하지 마시고, 타입 파라미터에 어떤 이름을 지어도 됩니다. TSession, TKey, TValue와 같은 더 읽기 쉬운 타입 파라미터 이름을 사용하는 것이 좋습니다.

또한 여러 타입 파라미터를 정의하고 쉼표로 구분할 수 있습니다.

class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
}

제네릭 클래스를 인스턴스화합니다.

尖括号에 실제 타입을 지정하여 제네릭 클래스의 인스턴스를 생성할 수 있습니다. DataStore의 인스턴스를 생성하겠습니다.

DataStore<string> store = new DataStore<string>();

위에서 string을 인스턴스 생성 시 괄호로 지정하면, T는 컴파일 시 string으로 대체되어 전체 클래스에서 사용된 T의 모든 타입을 대체합니다. 따라서 Data 속성의 타입은 string입니다.

아래 그림은 제네릭의 작동 방식을 설명합니다.

Data属性에 문자열 값을 할당할 수 있습니다. 문자열 이외의 값을 할당하려고 시도하면 컴파일 시 오류가 발생합니다.

DataStore<string> store = new DataStore<string>();
store.Data = "Hello World!";//obj.Data = 123; //컴파일 시 오류

다른 객체에 다른 데이터 타입을 지정할 수 있습니다. 예를 들어 다음과 같이 합니다.

DataStore<string> strStore = new DataStore<string>();
strStore.Data = "Hello World!";
//strStore.Data = 123; // 컴파일 시 오류
DataStore<int> intStore = new DataStore<int>();
intStore.Data = 100;
//intStore.Data = "Hello World!"; // 컴파일 시 오류
KeyValuePair<int, string> kvp1 = new KeyValuePair<int, string>();
kvp1.Key = 100;
kvp1.Value = "Hundred";
KeyValuePair<string, string> kvp2 = new KeyValuePair<string, string>();
kvp2.Key = "IT";
kvp2.Value = "Information Technology";

제네릭 클래스 특징

  • 제네릭 클래스는 재사용성을 높입니다. 타입이 많을수록 재사용성이 높아집니다. 그러나, 과도한 일반화는 코드를 이해하고 유지보수하기 어려워집니다.

  • 제네릭 클래스는 다른 제네릭이나 비제네릭 클래스나 추상 클래스의 기본 클래스가 될 수 있습니다.

  • 제네릭 클래스는 다른 제네릭이나 비제네릭 인터페이스, 클래스, 추상 클래스에서 자기 자신을 상속할 수 있습니다.

제네릭 필드

제네릭 클래스는 제네릭 필드를 포함할 수 있습니다. 그러나, 초기화할 수 없습니다.

class DataStore<T>
{
    public T data;
}

아래에서 제네릭 배열을 선언합니다.

class DataStore<T>
{
    public T[] data = new T[10];
}

제어 메서드

타입 파라미터를 사용하여 반환 타입이나 파라미터를 선언하는 메서드는 제어 메서드라고 합니다.

class DataStore<T>
{
    private T[] _data = new T[10];
    
    public void AddOrUpdate(int index, T item)
    {
        if(index >= 0 && index < 10)
            _data[index] = item;
    }
    public T GetData(int index)
    {
        if(index >= 0 && index < 10)
            return _data[index];
        else 
            return default(T);
    }
}

위의 AddorUpdate()과 GetData() 메서드는 제어 메서드입니다. item 파라미터의 실제 데이터 타입은 DataStore<T> 클래스를 인스턴스화할 때 지정됩니다. 예를 들어 다음과 같습니다.

DataStore<string> cities = new DataStore<string>();
cities.AddOrUpdate(0, "Mumbai");
cities.AddOrUpdate(1, "Chicago");
cities.AddOrUpdate(2, "London");
DataStore<int> empIds = new DataStore<int>();
empIds.AddOrUpdate(0, 50);
empIds.AddOrUpdate(1, 65);
empIds.AddOrUpdate(2, 89);

제어 파라미터 타입은 비제어 파라미터와 반환 타입이 있는 여러 파라미터와 함께 사용될 수 있습니다. 다음은 유효한 제어 메서드 오버로드입니다.

public void AddOrUpdate(int index, T data) { }
public void AddOrUpdate(T data1, T data2) { }
public void AddOrUpdate<U>(T data1, U data2) { }
public void AddOrUpdate(T data) { }

尖括号에서 메서드 이름을 사용하여 타입 파라미터를 지정하여 비제어 타입 클래스는 제어 메서드를 포함할 수 있습니다. 예를 들어 다음과 같습니다.

class Printer
{
    public void Print<T>(T data)
    {
        Console.WriteLine(data);
    }
}
Printer printer = new Printer();
printer.Print<int>(100);
printer.Print(200); // 지정된 값에 따라 추론
printer.Print<string>("Hello");
printer.Print("World!"); // 지정된 값에 따라 추론

Generics의 장점

  1. Generics는 코드의 재사용성을 높입니다. 다른 데이터 타입을 처리하는 코드를 작성할 필요가 없습니다.

  2. Generics는 타입 안전합니다. 정의된 데이터 타입과 다른 데이터 타입을 사용하려고 시도할 때 컴파일 시에 오류가 발생합니다.

  3. Generics는 패킹과 언패킹 가능성을 제거하여 성능 이점을 가지고 있습니다.