최신 C# 기능
C# 14
C# 최신 기능
C# 14: extension 키워드
C# 14: null 조건 할당 연산자
C# 14: nameof 향상
C# 14: Span 향상된 암묵적 변환
C# 14: Instance Operator
C# 14: 단순화된 람다 파라미터
C# 14: 새 field 키워드
C# 13
C# 13: params modifier 지원 확장
C# 13: lock 지시어 향상
C# 13: \e (새 Escape Sequence)
C# 13: 객체 초기화 식에 ^ 연산자 사용
C# 13: ref struct 타입 기능 향상
C# 13: partial 속성과 인덱서
C# 13: OverloadResolutionPriority
C# 12
C# 12: 클래스 Primary Constructor
C# 12: 컬렉션 식
C# 12: ref readonly 파라미터 (in)
C# 12: 람다식 디폴트 파라미터
C# 12: InlineArray 특성
C# 12: using alias 모든 타입
C# 12: Experimental, Interceptor
C# 11
C# 11: Raw String Literal
C# 11: 문자열 내삽 복수라인
C# 11: u8 접미어
C# 11: Generic Math 지원
C# 11: Generic Attribute
C# 11: 리스트 패턴
C# 11: 파일 로컬 타입
C# 11: required modifier
C# 11: Auto-default struct
C# 11: ReadOnlySpan 패턴 매칭
C# 11: 확장된 nameof 범위
C# 11: nint, nuint
C# 11: ref 필드
C# 11: 소문자 타입명 경고
C# 11: 향상된 method group 변환
C# 10
VS 2022 설치
C# 10 global using
C# 10 File-scoped Namespace
C# 10 향상된 문자열 내삽
C# 10 향상된 람다식 유추
C# 10 struct 기능 향상
C# 10 record struct
C# 10 확장된 속성패턴
C# 10 향상된 명료한 할당
C# 10 Destructor 기능 개선
C# 9.0
C# 9 레코드 타입
C# 9 init accessor
C# 9 최상위 프로그램
C# 9 향상된 패턴 매칭
C# 9 향상된 Target Typing
C# 9 공변 리턴 타입
C# 9 Native Int 타입
C# 8.0
C# 8 디폴트 인터페이스 멤버
C# 8 패턴 매칭
C# 8 Nullable Reference Type
C# 8 인덱싱과 슬라이싱
C# 8 비동기 스트림
C# 8 using 선언
C# 8 널 병합 할당자
C# 8 구조체 읽기 전용 멤버
C# 8 기타 기능들
C# 7.0
C# 7.0 새기능
C# 7.0 패턴 매칭
C# 7.0 튜플
C# 7.0 로컬 함수
C# 7.0 out 파라미터
C# 7.0 리터럴 표현
C# 7.0 Deconstructor
C# 7.0 ref return
C# 7.0 async 리턴타입
C# 7.0 Expression-bodied
C# 7.0 throw expression
C# 6.0
C# 6.0 새기능
C# 6.0 널 조건 연산자
C# 6.0 문자열 내삽
C# 6.0 Dictionary초기자
C# 6.0 nameof 연산자
C# 6.0 using static문
C# 6.0 catch블럭 await
C# 6.0 Exception 필터
C# 6.0 자동 속성 초기자
C# 6.0 읽기전용 자동 속성
C# 6.0 Expression-bodied

C# 11: Generic Attribute

지금까지 C#의 Attribute에서는 제네릭(Generics)을 사용할 수 없었다. C# 11에서는 Attribute 에서 파생되는 Custom Attribute 클래스에서 제네릭을 사용할 수 있도록 하였다. 즉, 이제 Attribute에서 제네릭을 사용하여 하나 이상의 타입 파라미터를 지정할 수 있으며, Reflection을 사용하여 Attribute 메타정보에서 타입 정보를 읽어 활용할 수 있게 되었다. C# 11 이전 버전에서 이러한 타입 정보는 Attribute 생성자에 전달하였고, 이를 Attribute 필드에 저장하여 사용하였다. 예를 들어, 아래 [예제A]는 C# 11 이전 버전에서 Attribute 생성자를 통해 타입 정보를 저장하는 예이고, [예제B]는 C# 11에서 제네릭을 사용하여 타입 정보를 지정하는 예이다. Generic Attribute를 사용하면 타입 정보를 간단하게 지정할 수 있을 뿐만 아니라, 또한 타입 파라미터에 대해 제약(Generic Constraint)을 지정하여 어떤 타입들이 지정될 수 있는 지를 정할 수 있다. C# 11 이전에는 생성자를 통해 Type 을 전달하였는데 이러한 방식은 컴파일 타임에 타입을 체크할 수 없는 반면, C# 11에는 제네릭의 Constraint를 사용함으로써 컴파일 타임에 보다 구체적으로 타입을 체크할 수 있게 되었다.

예제

// [예제A] C# 11 이전:

    // 타입 정보를 갖는 Attribute 정의
    class ValidatorAttribute : Attribute
    {
        private readonly Type type;

        public ValidatorAttribute(Type type)
        {
            this.type = type;
        }

        public Type ValidatorType => type; 
    }

    // 생성자 파라미터에 typeof를 사용하여 타입 전달
    [Validator(typeof(Customer))]
    class Customer
    {
        public int Id;
        public string Name;
        public string Email;
    }


// [예제B] C# 11 Generic Attribute:

    // Attribute에 제네릭 타입 T를 지정.
    // (추가적으로 타입 T의 Constrait를 지정할 수도 있다)
    class ValidatorAttribute<T> : Attribute
        where T : class
    {
    }

    // Attribute를 사용할 때 타입을 지정
    [Validator<Customer>]
    class Customer
    {
        public int Id;
        public string Name;
        public string Email;
    }


C# 11: Generic Attribute 정보 사용

Generic Attribute는 Attribute의 메타정보에 더하여 추가적으로 타입 정보를 저장한다. 이 타입 정보는 .NET Reflection을 사용하여 읽혀 질 수 있고, 이를 기반으로 다른 코드를 실행하도록 할 수 있다. 아래 예제는 위에서 정의한 ValidatorAttribute를 사용하여 타입 T에 지정된 클래스 타입에 따라 라는 Validator 객체를 생성하는 샘플 코드이다.

예제

// [예제A] C# 11 이전:

    public IValidator GetValidator(object obj)
    {
        // 해당 객체가 ValidatorAttribute를 가지고 있는 지 체크
        var attr = (ValidatorAttribute) obj.GetType()
            .GetCustomAttributes(typeof(ValidatorAttribute), true)
            .FirstOrDefault();

        if (attr != null && attr.ValidatorType != null)
        {
            // ValidatorAttribute가 있으면 ValidatorType 별로 
            // 다른 Validator를 생성하여 리턴함.
            if (attr.ValidatorType == typeof(Customer))
            {
                return (IValidator) Activator.CreateInstance(typeof(CustomerValidator));
            }
            // else if ...
        }
        return null;
    }    
    
    interface IValidator { }
    class CustomerValidator : IValidator { }


// [예제B] C# 11 Generic Attribute:

    public IValidator GetValidator(object obj)
    {
        // 해당 객체가 ValidatorAttribute를 가지고 있는 지 체크
        var attr = obj.GetType()
            .GetCustomAttributes(typeof(ValidatorAttribute<>), true)
            .FirstOrDefault();

        if (attr != null)
        {
            // ValidatorAttribute가 있으면 GenericTypeArguments를 체크하여
            // 타입 정보를 읽어, 이에 기초하여 적절한 Validator를 생성함.
            var validatorType = attr.GetType().GenericTypeArguments.First();

            if (validatorType == typeof(Customer))
            {
                return (IValidator)Activator.CreateInstance(typeof(CustomerValidator));
            }
            // else if ...
        }
        return null;
    }


C# 11: Generic Attribute 타입 아규먼트 제약

Generic Attribute는 타입 T 아규먼트에 다음과 같은 타입을 사용할 수 없다: (1) dynamic (2) string? (또는 null reference type) (3) (int x, int y) 포맷의 튜플 위와 같은 타입을 사용할 수는 없지만, 대체 타입으로 각각 object (dynamic의 기저 타입), string, ValueTuple 등을 사용할 수 있다.