where 키워드란?
제네릭 프로그래밍에서 중요한 역할을 하며, 제네릭 타입에 대한 제약 조건을 지정할 때 사용됩니다.
where키워드를 사용함으로써 컴파일 타임에 타입의 특정 특성을 보장할 수 있게 해줍니다. 예를 들어, 특정 타입이 반드시 클래스여야 하거나 특정 인터페이스를 구현해야 한다는 등의 조건을 걸 수 있습니다.
where 키워드 예시
형식 | 설명 | 예시 |
where T : class | 제네릭 타입 매개변수가 클래스여야 함 | where T : class |
where T : struct | 제네릭 타입 매개변수가 구조체여야 함 | where T : struct |
where T : new() | 제네릭 타입 매개변수가 매개변수가 없는 기본 생성자를 가져야 함 | where T : new() |
where T : SomeBaseClass | 제네릭 타입 매개변수가 특정 클래스를 상속받아야 함 | where T : MonoBehaviour |
where T : SomeInterface | 제네릭 타입 매개변수가 특정 인터페이스를 구현해야 함 | where T : IComparable |
where T : U | 제네릭 타입 매개변수가 다른 제네릭 타입 매개변수 U를 상속받아야 함 | where T : MonoBehaviour, new() |
where T : class, SomeInterface, new() | 여러 제약을 조합하여 사용할 수 있음 | where T : class, IComparable, new() |
where T : class
제네릭 타입 매개변수가 클래스여야 합니다.
public class MyClass<T> where T : class
{
// 클래스 제약을 가진 제네릭 클래스의 예시
}
// MyClass 클래스를 활용한 코드
MyClass<string> myStringClass = new MyClass<string>(); // "가능", string은 참조 타입(클래스)이다
MyClass<int> myIntClass = new MyClass<int>(); // "에러", int는 클래스가 아님, 값 타입(구조체)이다
where T : struct
제네릭 타입 매개변수가 구조체여야 합니다.
public class MyStructContainer<T> where T : struct
{
private T _value;
public MyStructContainer(T value)
{
_value = value;
}
}
// MyStructContainer 클래스를 활용한 코드
MyStructContainer<int> intContainer = new MyStructContainer<int>(10); // 가능
MyStructContainer<string> stringContainer = new MyStructContainer<string>("Hello"); // 에러: string은 구조체가 아님
where T : new()
제네릭 타입 매개변수가 매개변수가 없는 기본 생성자를 가져야 합니다.
public class Factory<T> where T : new()
{
public T CreateInstance()
{
return new T();
}
}
// Factory 클래스를 활용한 코드
Factory<int> intFactory = new Factory<int>(); // 가능: int는 기본 생성자를 가짐
Factory<string> stringFactory = new Factory<string>(); // 가능: string은 기본 생성자를 가짐
기본 생성자(Default Constructor)는 매개변수를 받지 않고 객체를 생성하는 생성자를 말합니다. 기본 생성자는 클래스에 직접 정의되지 않은 경우에도 컴파일러에 의해 자동으로 생성됩니다. 하지만, 클래스에 다른 생성자가 명시적으로 정의된 경우에는 기본 생성자가 자동으로 생성되 않습니다.
기본 생성자가 생성되는 경우는 다음과 같습니다.
public class MyClass
{
// 기본 생성자
public MyClass()
{
// 초기화 코드
}
}
public class MyClass
{
// 기본 생성자는 컴파일러에 의해 자동으로
// 만들어진다
}
반면에, 다음과 같이 매개변수를 받는 생성자만 있는 경우에는 기본 생성자가 자동으로 생성되지 않습니다.
public class MyClass
{
// 매개변수를 받는 생성자
public MyClass(int value)
{
// 초기화 코드
// 이때는 기본 생성자가 생성되지 않는다.
}
}
🔽객체에 대한 내용은 아래 포스팅을 참고해주세요!
where T : SomeBaseClass
제네릭 타입 매개변수가 특정 클래스를 상속받아야 합니다.
public class BaseClass
{
// 기본 클래스의 내용
}
public class DerivedClass : BaseClass
{
// 파생 클래스의 내용
}
public class MyClass<T> where T : BaseClass
{
// SomeBaseClass 제약을 가진 제네릭 클래스의 예시
}
// MyClass 클래스를 활용한 코드
MyClass<BaseClass> baseClassInstance = new MyClass<BaseClass>(); // 가능
MyClass<DerivedClass> derivedClassInstance = new MyClass<DerivedClass>(); // 가능
MyClass<int> intClassInstance = new MyClass<int>(); // 불가능: int는 SomeBaseClass를 상속받지 않고 있기 때문
where T : SomeInterface
제네릭 타입 매개변수가 특정 인터페이스를 구현해야 합니다.
public interface IMyInterface
{
// 인터페이스의 내용
}
public class MyClass<T> where T : IMyInterface
{
// SomeInterface 제약을 가진 제네릭 클래스의 예시
}
// MyClass 클래스를 활용한 코드
MyClass<IMyInterface> interfaceInstance = new MyClass<IMyInterface>(); // 가능
MyClass<int> intInstance = new MyClass<int>(); // 불가능: int는 IMyInterface를 구현하고 있지 않기 때문
where T : U
제네릭 타입 매개변수가 다른 제네릭 타입 매개변수 U를 상속받아야 합니다.
public class BaseClass
{
// 기본 클래스의 내용
}
public class DerivedClass : BaseClass
{
// 파생 클래스의 내용
}
public class MyClass<T, U> where T : U
{
// T가 U를 상속받는 경우의 예시
}
// MyClass 클래스를 활용한 코드
MyClass<DerivedClass, BaseClass> derivedInstance = new MyClass<DerivedClass, BaseClass>(); // 가능
MyClass<BaseClass, DerivedClass> baseInstance = new MyClass<BaseClass, DerivedClass>(); // 불가능: BaseClass가 DerivedClass를 상속받지 않고 있기 때문
where T : class, SomeInterface, new()
여러 제약을 조합하여 사용할 수 있습니다.