반응형
제네릭(Generic)은 클래스, 인터페이스, 메서드를 작성할 때 사용될 데이터 타입을 미리 지정하지 않고, 사용 시점에 원하는 타입을 지정할 수 있게 해주는 기능이다. 한 마디로, 여러가지 타입을 한방에 대응 가능!

 

1. 제네릭의 장점
- 타입의 안정성: 컴파일 시점에 타입 체크를 수행하여 런타임 오류를 줄입니다.
- 코드의 재사용: 다양한 데이터 타입에 대해 동일한 로직을 적용할 수 있습니다.
- 성능 향상: 박싱/언박싱 연산을 줄여 성능을 개선합니다.
- 명확성: 코드의 의도를 더 명확하게 표현할 수 있습니다.

 

2. 제네릭 클래스
제네릭 클래스는 하나 이상의 타입 매개변수를 가진 클래스입니다.
public class GenericList<T>
{
    private List<T> items = new List<T>();

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

    public T GetItem(int index)
    {
        return items[index];
    }
}

// 사용 예
var intList = new GenericList<int>();
intList.Add(10);

var stringList = new GenericList<string>();
stringList.Add("Hello");​

 

2. 제네릭 메서드
제네릭 메서드는 타입 매개변수를 가진 메서드입니다.
public void Print<T>(T inputMessage)
{
    Debug.Log(inputMessage);
}

void Start()
{
    Print<int>(30);
    Print<string>("Hello");
}​

public static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

// 사용 예
int x = 5, y = 10;
Swap(ref x, ref y);​

 

3. 제네릭 제약조건
제네릭 타입에 특정 조건을 부여할 수 있습니다.
public class Calculator<T> where T : struct, IComparable<T>
{
    public T Max(T a, T b)
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
}

// 사용 예
var calc = new Calculator<int>();
int max = calc.Max(5, 10);​

[주요 제약조건]
• where T : struct - 값 타입만 허용
• where T : class - 참조 타입만 허용
• where T : new() - 매개변수 없는 생성자가 있어야 함
• where T : BaseClass - 특정 기본 클래스의 파생 클래스만 허용
• where T : IInterface - 특정 인터페이스를 구현한 타입만 허용

 

4. 게임 개발에서의 제네릭 활용 예시
[오브젝트 풀링]
public class ObjectPool<T> where T : MonoBehaviour
{
    private Queue<T> pool = new Queue<T>();
    private T prefab;

    public ObjectPool(T prefab, int initialSize)
    {
        this.prefab = prefab;
        for (int i = 0; i < initialSize; i++)
        {
            CreateNewObject();
        }
    }

    public T Get()
    {
        if (pool.Count == 0)
            CreateNewObject();
        return pool.Dequeue();
    }

    public void Return(T obj)
    {
        obj.gameObject.SetActive(false);
        pool.Enqueue(obj);
    }

    private void CreateNewObject()
    {
        T newObj = UnityEngine.Object.Instantiate(prefab);
        newObj.gameObject.SetActive(false);
        pool.Enqueue(newObj);
    }
}

// 사용 예
ObjectPool<Bullet> bulletPool;
bulletPool = new ObjectPool<Bullet>(bulletPrefab, 30);

Bullet bullet = bulletPool.Get();
// 총알 사용 로직
bulletPool.Return(bullet);​


[데이터 관리 시스템]

public interface IGameData
{
    string Id { get; }
}

public class DataManager<T> where T : IGameData
{
    private Dictionary<string, T> dataMap = new Dictionary<string, T>();

    public void AddData(T data)
    {
        dataMap[data.Id] = data;
    }

    public T GetData(string id)
    {
        return dataMap.TryGetValue(id, out T data) ? data : default;
    }
}

// 사용 예
public class ItemData : IGameData
{
    public string Id { get; set; }
    public string Name { get; set; }
    public int Power { get; set; }
}

DataManager<ItemData> itemManager = new DataManager<ItemData>();
itemManager.AddData(new ItemData { Id = "sword1", Name = "Steel Sword", Power = 10 });

ItemData sword = itemManager.GetData("sword1");

 

5. 주의사항
- 과도한 사용 주의: 모든 것을 제네릭으로 만들면 코드가 복잡해질 수 있다.
- 성능 고려: 매우 작은 규모의 작업에 제네릭을 사용하면 오히려 성능 저하가 있을 수 있다.
- 제약조건 활용: 적절한 제약조건을 사용하여 타입의 특성을 최대한 활용할 수 있다.

 

 

[정리하며]

제네릭은 C#에서 타입 안전성코드 재사용성을 높이는 강력한 도구입니다. 특히 게임 개발에서 다양한 타입의 데이터와 객체를 다룰 때 매우 유용합니다. 오브젝트 풀링, 데이터 관리, 알고리즘 구현 등 다양한 영역에서 제네릭을 활용하면 더 견고하고 유연한 코드를 작성할 수 있습니다.

반응형

+ Recent posts