본문 바로가기
책/Effective C#

Effective C# - Item23 타입 매개변수에 대해 메서드 제약 조건을 설정하려면 델리게이트를 활용하라

by 코딩하는 돼징 2023. 12. 15.
반응형

C#에서 제약 조건은 일정한 범위 내에서 사용 가능하며 기본적으로 클래스의 상속, 인터페이스 구현, 생성자 제약 등의 형태로 제한이 이루어진다. 몇 가지 경우에는 런타임에 제약 조건을 체크할 수 있는 기능이 제공되지 않기 때문에 컴파일 타임에만 확인할 수 있는 제약을 설정하는 것이 어려울 수 있다.

01 베이스 클래스의 타입이나 특정 인터페이스로 제약 조건
예를 들어, 특정 인터페이스를 구현하도록 하는 것은 가능하지만 인터페이스의 메서드 시그니처를 강제하거나 특정한 static 메서드를 반드시 구현하도록 하는 것은 어렵다.
02 class 타입이나 struct 타입으로 형태 제한
클래스 또는 구조체로 제한하는 것은 가능하지만 특정 메서드나 생성자의 존재를 강제하는 것은 기본 제약 조건에 포함되지 않다.
03 매개변수가 없는 생성자 제약:
일반적으로 매개변수가 없는 생성자를 강제할 수 있지만 특정한 로직이나 초기화를 수행하는 생성자를 강제하는 것은 불가능하다.

 

제한적이기만 인터페이스를 통해서 제약 조건을 설정할수있지만 추가적으로 해야 할 작업이 너무 많기도하고 기본적인 구조도 해칠 수 있다. 이를 위해 메서드의 원형에 부합하는 델리게이트를 활용하는 것이 좋다.


인터페이스를 이용한 메서드 제약

01 IAdd<T> 인터페이스 생성

public interface IAdd<T>
{
    T Add(T other);
}

02 AddExample클래스와 제약 조건 설정

Add<T>에서 Add메서드를 사용하기 위해서는 제네릭 형식 T가 IAdd<T>인터페이스를 구현해야한다는 제약 조건 설정

public static class AddExample
{
    public static T Add<T>(T left, T right) where T : IAdd<T>
    {
        // IAdd<T> 인터페이스의 Add 메서드를 호출하여 두 값을 더하고 결과를 반환
        return left.Add(right);
    }
}

03 IAdd<T>를 구현하는 MyClass클래스 생성

public class MyClass : IAdd<MyClass>
{
    int value;
    public MyClass(int value)
    {
        this.value = value;
    }
    public MyClass Add(MyClass A)
    {
        return new MyClass(this.value);
    }
}

04 사용 예시

MyClass a = new MyClass(32);
MyClass b = new MyClass(75);
MyClass result = AddExample.Add<MyClass>(a, b);

 

 

이렇게 개발해서는 사용자아게 혼란만 가중시킬 뿐이다. 이에 우리는 다른 방법인 델리게이트를 작성할 것이다.


델리게이트를 이용한 메서드 제약

01 제약 조건으로 설정하고 싶은 메서드의 원형에 부합하는 델리게이트 찾기

적절한 메서드의 원형을 고안하고 이를 델리게이트 타입(Action, Func, Predicate)으로 정의한다.

ublic delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

02 델리게이트의 인스턴스 제네릭 메서드의 매개변수로 추가

람다 표현식을 이용하여 제네릭 클래스가 호출할 AddFunc메서드 정의

public static class Example
{
    public static T Add<T>(T left, T right, Func<T, T, T> AddFunc) => AddFunc(left, right);
}

위의 모드에서 Add메서드는 두 개의 값과 델리게이트를 받아들려 델리게이트를 실행하고 그 결과를 반환한다.


03 해당 클래스의 사용자는 람다 표현식을 인자로 전달

int a = 4;
int b = 9;

// 사용자는 람다 표현식을 전달하여 덧셈 동작을 정의
int sum = Example.Add(a, b, (x, y) => x + y);

결론

클래스나 인터페이스를 사용하여 제약 조건을 설정하는 것은 일반적으로 설계의 특성을 더 잘 드러내고 컴파일 타임에서 타입 안전성과 강력한 형식 지정을 제공한다. 특히 제네릭 클래스나 메서드가 특정 인터페이스를 필요로 할 때 해당 인터페이스를 구현한 새로운 타입을 만들어 사용할 수 있다.
하지만 간단한 제네릭 메서드를 사용하는 경우나 제약 조건을 매번 구현하는 것이 부담스러운 경우에는 델리게이트를 사용하는 것이 편리할 수 있다. 델리게이트를 활용하면 런타임에 동적으로 메서드를 전달할 수 있어 유연성이 향상된다.
따라서 어떤 방법을 선택할지는 상황과 요구 사항에 따라 다를 것이며 설계의 목적과 편의성을 고려하여 적절한 방법을 선택해야 한다. 

 

 

 

 

 

본 게시글은 Effective C#을 읽고 정리하였습니다.

 

반응형

댓글