| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- git
- Toy Project
- C++
- BOJ
- Data Structure
- Online Judge
- PS
- multi-thread
- System Programming
- Network Programming
- Unity
- 독서
- c#
- Today
- Total
I'm FanJae.
[20260513] C# ( 제네릭 제약 ) 본문
1. C# 정리 ( 제네릭 제약 )
(1) 정의
- 제네릭(Generic)은 자료형을 나중에 결정할 수 있게 해주는 문법이다.
List <int>
List <string>
- 하지만 제네릭은 어떤 타입이 들어올지 컴파일 시점에 정해지지 않기 때문에, 아무 타입이나 사용하면 문제가 발생할 수 있다.
- 이를 해결하기 위해 사용하는 것이 제네릭 제약 조건(Generic Constraint)이다.
- 제약 조건은 다음과 같이 사용한다.
where T : 조건
- 즉, T에는 특정 조건을 만족하는 타입만 사용할 수 있다.
(2) 필요한 이유
- 제네릭은 타입을 일반화하는 문법이지만, 아무 타입이나 허용하면 원하는 기능을 보장할 수 없다.
- 예를 들어, 공격 기능을 실행해야 하는 상황이라고 가정한다.
class Attacker<T>
{
}
- 이 경우 아래와 같은 타입도 허용하게 된다.
Attacker<int>
Attacker<string>
- 하지만 int나 string은 공격 기능이 존재하지 않는다.
- 따라서 다음과 같이 제한을 걸어야 한다.
class Attacker<T> where T :IAttackable
{
}
- 이제 IAttackable 인터페이스를 구현한 타입만 사용할 수 있다.
- 즉, 제네릭에 대하여 제약을 걸어두어 아래와 같은 특징이 생긴다.
(1) 공격 가능한 객체만 허용된다.
(2) 잘못된 타입 사용을 방지한다.
(3) 기능 사용 가능을 보장한다.
(3) Generic 제약 조건 종류
3-1. 참조 타입 제한 (class)
class ReferenceOnly <T> where T :class
{
}
- class 제약은 참조 타입만 허용한다.
(1) 가능
ReferenceOnly<string> // 참조 타입이므로, 사용이 가능함.
(2) 불가능
ReferenceOnly<int> // 값 타입이므로, 사용이 불가능함.
(3) 주의점
- class는 내가 만든 클래스만 의미하는 것이 아니다. 모든 참조 타입을 의미한다.
- 예를들어, string 역시 참조 타입이므로 사용 가능하다.
3-2. 값 타입 제한 (struct)
class ValueOnly<T> where T : struct
{
}
- struct 제약은 값 타입만 허용한다.
(1) 가능
ValueOnly<int> // 값 타입이므로, 사용이 가능함.
ValueOnly<bool> // 값 타입이므로, 사용이 가능함.
(2) 불가능
ValueOnly <string> // 참조 타입이므로, 사용이 불가능함
3-3. 기본 생성자 제한 ( new() )
class Factory<T> where T : new()
{
public T CreateInstance()
{
return new T();
}
}
- new() 제약은 매개변수가 없는 public 기본 생성자가 있는 타입만 허용한다.
- 이 제약이 있어야 제네릭 내부에서 다음 코드 사용이 가능하다.
new T()
- 제약이 없다면 컴파일 오류가 발생한다.
3-4. 부모 클래스 제한
class Character
{
public string Name { get; set; }
}
class Warrior : Character { }
class CharacterManager<T> where T : Character
{
public void PrintName(T character)
{
Console.WriteLine(character.Name);
}
}
Where T : Character
- 여기서 Where T : Character 가 의미하는 바는 아래와 같다.
(1) Character 타입이면 사용이 가능하다.
(2) Character를 상속한 자식 클래스이라면 사용 가능하다.
- 따라서 CharacterManager 의 PrintName()에서 Name 프로퍼티 사용이 보장된다.
3-5. 인터페이스 제한
interface IAttackable
{
void Attack();
}
class Monster :IAttackable
{
public void Attack()
{
}
}
class Attacker<T> where T : IAttackable
{
}
- 인터페이스 제약은 특정 기능 구현 여부를 제한할 때 자주 사용한다.
3-6. 여러 개 제약 조건
- 제약 조건은 여러 개를 동시에 사용할 수도 있다.
where T : Gameobject, IDamageable
class Enemy : GameObject, IDamageable // 클래스, 인터페이스 순서
{
public void TakeDamage()
{
}
}
(1) 가능
DamageHandler <Enemy>
- Enemy는 GameObject을 상속하고 IDamageable을 구현하기 때문에 이 조건에 만족한다.
(2) 불가능
DamageHandler <string>
DamageHandler <GameObject>
- GameObject는 IDamageable을 구현하지 않았기 때문에 사용할 수 없다.
(4) Generic 제약 조건의 장점
① 잘못된 타입 사용 방지
- 컴파일 단계에서 잘못된 타입 사용을 막을 수 있다.
where T : IDamageble
- 이 제약이 있다면, 데미지를 처리할 수 없는 객체는 사용할 수 없다.
② 기능 사용 보장
where T : Character
- 이 경우 Character 의 프로퍼티와 메서드를 안전하게 사용할 수 있다.
character.Name
③ 코드 의도가 명확해진다.
where T : MonoBehaviour, IDamageble
- MonoBehaviour(Unity 컴포넌트)이면서, 데미지를 받을 수 있는 객체만 허용한다.
(5) Generic 제약 조건 사용 시 주의점
① 클래스 제약은 먼저 작성
where T : GameObject, IDamageable
- 클래스 제약을 먼저 쓰고, 인터페이스 제약을 뒤에 작성해야 한다.
② new() 제약은 마지막에 작성한다.
where T : Character(), new()
- new() 는 항상 마지막에 작성해야 한다.
③ class 는 참조 타입을 전체 의미한다.
where T : class
- 사용자 정의 클래스만 허용이 아니다.
- string 과 같은 내용도 포함된다.
(6) Unity에서 Generic 제약 조건을 사용하는 이유
- Unity는 Component 기반 구조를 사용한다.
- 즉, 공격 가능, 이동 가능, 데미지 가능, 상호 작용 가능과 같은 기능 중심 설계가 많다.
- 이에 따라서, Generic Constraint를 주로 사용한다.
① 클래스 제약은 먼저 작성
where T : Component
- Unity 컴포넌트만 허용한다.
GetComponent<T>() // 대표적인 예시다.
- Component를 가져오는 자료형에 대해서 아래와 같은 작업은 의미가 없다.
GetComponent<int>()
- int는 Unity Component가 아니기 때문이다.
② 기능 기반 제한
where T : IDamageble
- 데미지를 받을 수 있는 객체만 허용한다.
③ 여러 기능 조합
where T : MonoBehaviour, IDamageable
- Unity Component 이면서, 동시에 데미지를 받을 수 있어야 한다.
- 이처럼, 특정 기능을 가진 객체만 안전하게 처리할 수 있다.
(7) 정리
- Generic Constraint는 제네릭에 사용할 수 있는 타입 범위를 제한하는 기능이다.
1) 잘못된 타입 사용을 방지할 수 있다.
2) 기능 사용 가능을 보장한다.
3) 코드 안정성이 증가한다.
4) Unity 기능 기반 설계를 지원한다.
- 특히 Unity 에서는 아래와 같은 형태로 자주 사용된다.
where T : Component
where T : MonoBehaviour
where T : IDamageable
'Unity > Unity 초격차캠프' 카테고리의 다른 글
| [20260513] C# ( Hash 기반 자료구조의 주의점과 이를 해결하는 기법) (0) | 2026.05.13 |
|---|---|
| [20260513] C# ( HashTable 와 Dictionary의 공통점, 차이점 ) (0) | 2026.05.13 |
| [20260512] C# ( Boxing & UnBoxing ) (0) | 2026.05.12 |
| [20260512] C# ( List ) (0) | 2026.05.12 |
| [20260512] C# ( 제네릭 프로그래밍 ) (0) | 2026.05.12 |