참고 영상: https://www.youtube.com/watch?v=G3Edvvp_YpA&list=PLVsNizTWUw7GN8wPRhclbKuQa9aI9Cj2V&index=12
형식 매개 변수 T가 아무 타입이나 될 수 있지만, 어떤 기능을 사용하려면 특정 타입 또는 조건을 만족해야 할 때가 있다.
이때 제약을 걸면 T가 특정 조건을 갖춰야만 사용할 수 있다.
문법: where 형식매개변수: 제약조건
| 제약 | 설명 |
|---|---|
| where T : struct | T는 값 형식이어야 한다. |
| where T : class | T는 참조 형식이어야 한다. |
| where T : new() | T는 반드시 매개 변수가 없는 생성자가 있어야 한다. |
| where T : 기반 클래스 이름 | T는 명시한 기반 클래스의 파생 클래스여야 한다. |
| where T : 인터페이스 이름 | T는 명시한 인터페이스를 반드시 구현해야 한다. 인터페이스에는 여러 개의 인터페이스를 명시할 수 있다. |
| where T : U | U는 클래스 또는 인터페이스가 될 수 있으며, T는 U로부터 상속받은 클래스여야 한다. |
⚔ where T : struct - T를 값 형식으로 제약
class MyValue<T> where T : struct
{
public void PrintValue()
{
T value = default(T); // default(T) - T가 float이면 value는 0.0f
Console.WriteLine(value);
}
}
// 사용 예시
var myValue = new MyValue<int>();
default(T)는 T가 어떤 타입이든지 그 타입에 맞는 초기값을 갖는다.
ex) T가 int인 경우 default(T)는 0이다.
⚔ where T : class - T를 참조 형식으로 제약
class MyReference<T> where T : class
{
public void PrintTypeName(T obj)
{
Console.WriteLine(obj.GetType().Name);
}
}
// 사용 예시
var myReference = new MyReference<string>();
⚔ where T : new() - T는 매개변수 없는 생성자를 가져야 한다.
T타입 객체를 생성하고 싶을 때 해당 객체가 T() 생성자를 가지고 있다는 보장이 없기때문에 new T()를 제약 없이 사용하면 컴파일 에러가 발생한다.
new T()를 사용하려면 where T : new() 제약이 필요하다.
class Creatable<T> where T : new()
{
public T Create()
{
return new T();
}
}
// 사용 예시
class Person
{
public string Name { get; set; } = "Unknown";
}
var creator = new Creatable<Person>();
var p = creator.Create();
⚔ where T : 기반 클래스 이름 - T는 지정한 클래스 또는 파생 클래스여야 한다.
class Animal
{
public virtual void Say() => Console.WriteLine("Animal sound");
}
class Dog : Animal
{
public override void Say() => Console.WriteLine("Mung");
}
class AnimalSoundBox<T> where T : Animal
{
public void MakeSound(T animal)
{
animal.Say();
}
}
// 사용 예시
var animalSoundBox = new AnimalSoundBox<Dog>(); // 이 순간 MakeSound(Dog animal)로 변경
animalSoundBox.MakeSound(new Dog());
⚔ where T : 인터페이스 이름 - T는 명시한 인터페이스를 구현해야 한다.
interface IWalkable
{
void Walk();
}
class Human : IWalkable
{
public void Walk() => Console.WriteLine("Walking...");
}
class Walker<T> where T : IWalkable
{
public void StartWalk(T obj)
{
obj.Walk();
}
}
// 사용 예시
var walker = new Walker<Human>();
walker.StartWalk(new Human()); // 출력: Walking...
⚔ where T : U - T가 반드시 U를 상속하거나, U를 구현한 타입이어야 한다.
U가 클래스라면, T는 U를 상속해야 한다.
U가 인터페이스라면, T는 U를 구현해야 한다.
class Animal
{
public void Eat() => Console.WriteLine("Eating");
}
class Dog : Animal
{
public void Bark() => Console.WriteLine("Woof!");
}
// T는 반드시 U를 상속해야 함
class AnimalTrainer<T, U> where T : U
{
public void Train(T animal)
{
Console.WriteLine("Training...");
}
}
// 사용 예시
var trainer = new AnimalTrainer<Dog, Animal>();
trainer.Train(new Dog());