제네릭 타입의 제한

Shy·2025년 3월 17일

C#

목록 보기
27/27

제네릭 타입 제한

전 시간, 제네릭 타입의 제한에 대한 보충...

제네릭 타입의 제한(제약 조건, Constraints) 을 지정할 때,
값 형식(struct)인지 참조 형식(class)인지, 특정 인터페이스를 구현했는지 등을 제한할 수 있다.

이를 활용하면 잘못된 타입이 들어오는 것을 방지하고, 타입 안전성을 확보할 수 있다.


1. 제네릭 타입 제한(제약 조건) 종류

제한 조건설명
where T : structT는 반드시 값 타입(Struct) 이어야 한다.
where T : classT는 반드시 참조 타입(Class, Interface, Delegate, Array) 이어야 한다.
where T : new()T는 반드시 매개변수가 없는 기본 생성자를 가져야 한다.
where T : SomeBaseClassT는 SomeBaseClass를 상속한 클래스여야 한다.
where T : SomeInterfaceT는 SomeInterface를 구현한 타입이어야 한다.
where T : UT는 반드시 U를 상속하거나 구현해야 한다.

2. where T : struct (값 타입 제한)

  • T는 반드시 값 타입(Struct) 이어야 한다.
  • class 타입을 넣으면 컴파일 오류 발생
using System;

public class MyClass { } // 참조 타입

public struct MyStruct { } // 값 타입

public class ValueTypeExample<T> where T : struct
{
    public void Print(T value)
    {
        Console.WriteLine(value);
    }
}

class Program
{
    static void Main()
    {
        ValueTypeExample<int> intExample = new ValueTypeExample<int>(); // ✅ 가능 (int는 struct)
        intExample.Print(100); // 100 출력

        ValueTypeExample<MyStruct> structExample = new ValueTypeExample<MyStruct>(); // ✅ 가능
        structExample.Print(new MyStruct());

        // ValueTypeExample<MyClass> classExample = new ValueTypeExample<MyClass>(); // ❌ 오류 발생 (MyClass는 참조형)
    }
}
100
  • 값 타입(struct)만 허용되고, 클래스(class)를 사용하면 컴파일 오류가 발생한다.

3. where T : class (참조 타입 제한)

  • T는 반드시 참조 타입(Class, Interface, Delegate, Array) 이어야 한다.
  • struct 타입을 넣으면 컴파일 오류 발생
using System;

public class MyClass { } // ✅ 참조 타입

public struct MyStruct { } // ❌ 값 타입

public class ReferenceTypeExample<T> where T : class
{
    public void Print(T value)
    {
        Console.WriteLine(value);
    }
}

class Program
{
    static void Main()
    {
        ReferenceTypeExample<MyClass> classExample = new ReferenceTypeExample<MyClass>(); // ✅ 가능
        classExample.Print(new MyClass());

        ReferenceTypeExample<string> stringExample = new ReferenceTypeExample<string>(); // ✅ 가능
        stringExample.Print("Hello");

        // ReferenceTypeExample<MyStruct> structExample = new ReferenceTypeExample<MyStruct>(); // ❌ 오류 발생
    }
}
  • 참조 타입(class)만 허용되고, 값 타입(struct)을 사용하면 컴파일 오류 발생!

4. where T : new() (기본 생성자 제한)

  • T는 매개변수가 없는 기본 생성자가 있어야 한다.
  • abstract classinterface는 사용 불가
  • new()가 있는 경우, struct를 사용할 때 Nullable<T>을 허용하지 않는다.
using System;

public class MyClass
{
    public MyClass() { } // 기본 생성자 ✅
}

public class MyClassWithParams
{
    public MyClassWithParams(int value) { } // 기본 생성자 없음 ❌
}

public class DefaultConstructorExample<T> where T : new()
{
    public T CreateInstance()
    {
        return new T(); // 매개변수 없는 생성자 호출 가능
    }
}

class Program
{
    static void Main()
    {
        DefaultConstructorExample<MyClass> obj1 = new DefaultConstructorExample<MyClass>(); // ✅ 가능
        MyClass instance1 = obj1.CreateInstance();

        // DefaultConstructorExample<MyClassWithParams> obj2 = new DefaultConstructorExample<MyClassWithParams>(); // ❌ 오류 발생
    }
}
  • 기본 생성자가 없는 클래스(MyClassWithParams)를 사용하면 컴파일 오류 발생!

5. where T : SomeBaseClass (상속 제한)

  • T는 반드시 SomeBaseClass를 상속해야 한다.
using System;

public class Animal { } // 부모 클래스

public class Dog : Animal { } // ✅ 상속받은 클래스
public class Cat : Animal { } // ✅ 상속받은 클래스
public class Car { } // ❌ 다른 클래스

public class AnimalExample<T> where T : Animal
{
    public void Print(T animal)
    {
        Console.WriteLine(animal.GetType().Name);
    }
}

class Program
{
    static void Main()
    {
        AnimalExample<Dog> dogExample = new AnimalExample<Dog>(); // ✅ 가능
        dogExample.Print(new Dog()); // Dog 출력

        AnimalExample<Cat> catExample = new AnimalExample<Cat>(); // ✅ 가능
        catExample.Print(new Cat());

        // AnimalExample<Car> carExample = new AnimalExample<Car>(); // ❌ 오류 발생 (Car는 Animal을 상속하지 않음)
    }
}
  • Animal을 상속한 Dog, Cat은 가능하지만, Car는 오류가 발생한다.

6. where T : SomeInterface (인터페이스 구현 제한)

  • T는 반드시 SomeInterface를 구현해야 한다.
using System;

public interface IMovable
{
    void Move();
}

public class Car : IMovable
{
    public void Move() => Console.WriteLine("Car is moving");
}

public class Animal { } // ❌ IMovable 미구현

public class MovableExample<T> where T : IMovable
{
    public void ExecuteMove(T obj)
    {
        obj.Move();
    }
}

class Program
{
    static void Main()
    {
        MovableExample<Car> carExample = new MovableExample<Car>(); // ✅ 가능
        carExample.ExecuteMove(new Car());

        // MovableExample<Animal> animalExample = new MovableExample<Animal>(); // ❌ 오류 발생
    }
}
  • IMovable 인터페이스를 구현한 Car는 가능하지만, Animal은 오류가 발생한다.

7. where T : U (타입 상속 제한)

  • T는 반드시 U를 상속 또는 구현해야 한다.
using System;

public class BaseClass { }

public class DerivedClass : BaseClass { }

public class GenericExample<T, U> where T : U
{
    public void Print(T value)
    {
        Console.WriteLine(value.GetType().Name);
    }
}

class Program
{
    static void Main()
    {
        GenericExample<DerivedClass, BaseClass> example = new GenericExample<DerivedClass, BaseClass>(); // ✅ 가능
        example.Print(new DerivedClass());

        // GenericExample<BaseClass, DerivedClass> invalidExample = new GenericExample<BaseClass, DerivedClass>(); // ❌ 오류 발생
    }
}
  • TU를 상속해야 하는데, BaseClassDerivedClass를 상속하지 않으므로 오류가 발생한다.

정리

제한 조건설명
where T : structT는 값 타입(struct) 이어야 한다.
where T : classT는 참조 타입(class, interface, delegate, array) 이어야 한다.
where T : new()T는 매개변수 없는 기본 생성자를 가져야 한다.
where T : SomeBaseClassTSomeBaseClass를 상속해야 한다.
where T : SomeInterfaceTSomeInterface를 구현해야 한다.
where T : UT는 반드시 U를 상속 또는 구현해야 한다.
profile
신입사원...

0개의 댓글