C# 14 후보 기능에 대해 살펴봅니다

C# 언어의 새로운 기능은 아래 문서로 추적할 수 있어요.

여기서 Working Set C#을 보면 되는데 이 목록은 최종 릴리스 되기까지 추가되거나 삭제 될 수 있어요.

이 글은 슬로그 방식으로 계속 추적해서 작성될 예정입니다.

2개의 좋아요

이미 Visual Studio 2022 Preview 17.14 p1을 설치할 수 있으므로 Working Set C#의 아래 기능을 테스트해볼 수 있습니다.

속성의 field 키워드

프로퍼티에서 자동으로 생성되는 백킹 필드에 접근할 수 있도록 새로운 문맥 키워드 field를 도입하여 모든 프로퍼티를 확장하는 제안입니다. 이제 프로퍼티의 접근자에서는 field 키워드를 사용하여 백킹 필드를 참조할 수 있으며, 접근자는 본문이 없는 자동 접근자와 본문이 있는 접근자를 함께 사용할 수 있습니다.

이 기능을 통해 게터 또는 세터에서 추가 제어가 필요한 경우, 별도의 백킹 필드를 선언하지 않고도 field 키워드를 활용하여 관련 작업을 수행할 수 있습니다. 예를 들어, 지연 초기화, 기본값 설정, 값의 유효성 검사, 변경 알림 이벤트 발생 등을 간단하게 구현할 수 있습니다.

또한, field 키워드와 관련된 널 가능성 분석 및 초기화 규칙도 제시되어 있으며, 기존 코드와의 호환성을 고려한 설계가 이루어졌습니다. 이로써 프로퍼티의 사용이 더욱 유연해지고, 코드의 가독성과 유지보수성이 향상될 것으로 기대됩니다.

일류 Span 유형

이 제안은 C# 언어에서 Span와 ReadOnlySpan를 일급 지원하도록 하는 것입니다. 이를 통해 배열, Span, ReadOnlySpan, 문자열 사이에 새로운 암시적 변환을 도입하여 더 자연스럽고 효율적인 프로그래밍이 가능해집니다. 구체적으로, 단일 차원 배열을 Span나 ReadOnlySpan로, Span를 ReadOnlySpan로, 문자열을 ReadOnlySpan로 암시적으로 변환할 수 있도록 합니다. 이러한 변환은 오버로드 해석과 타입 추론 시에 고려되어 API의 중복을 줄이고 사용성을 높입니다.

또한, 확장 메서드 수신자에서도 암시적 Span 변환을 허용하여 Span과 ReadOnlySpan 타입 간의 상호 운용성을 향상시킵니다. 타입 추론 규칙도 업데이트하여 Span 관련 타입의 추론을 지원합니다. 이로 인해 일부 기존 코드에서 모호성이 발생할 수 있지만, 이는 워크어라운드나 API 수정으로 해결 가능합니다.

이 제안은 언어 버전 14부터 적용되며, 기존 코드와의 호환성을 고려하여 세부적인 규칙 변경과 잠재적인 문제점에 대한 논의도 포함하고 있습니다. 사용자 정의 변환과의 상호 작용, 변환 우선순위 등의 열린 질문도 제시되어 향후 개선 방향을 모색합니다.

nameof에 바인딩되지 않은 일반 유형

이 제안은 C#의 nameof 표현식에서 제네릭 타입의 미결합 형태를 사용할 수 있게 합니다. 예를 들어, nameof(List<>)를 사용하여 “List” 문자열을 얻을 수 있습니다. 이를 통해 불필요하게 제네릭 타입 인수를 지정해야 하는 번거로움을 제거했습니다. 또한, nameof 표현식에서 미결합 타입을 통해 멤버에 접근할 수 있어 nameof(A<>.B)는 "B"를 반환합니다.

일부 제한사항으로, 다른 제네릭 타입의 타입 인수로 미결합 타입을 중첩하여 사용하는 것은 지원되지 않습니다(예: A<B<>>). 부분적으로 미결합된 타입도 지원되지 않습니다(예: Dictionary<int,>). 이러한 기능을 지원하기 위해 구문과 의미 체계에 대한 세부적인 변경 사항이 포함되어 있습니다.

데이터 섹션의 문자열 리터럴을 UTF8로 설정합니다.

이 실험적인 Roslyn 기능은 C# 프로그램에서 문자열 리터럴을 PE 파일에 내보내는 방식을 변경합니다. 기본적으로 문자열 리터럴은 2^24바이트 제한이 있는 UserString 힙에 저장되며, 이 한계를 넘으면 CS8103 컴파일러 오류가 발생합니다. 이 기능을 활성화하면 문자열 리터럴을 UTF-8로 인코딩하여 다른 섹션에 내보내 이 제한을 우회할 수 있습니다. 기능 플래그는 임계값을 지정하여 그 길이보다 긴 문자열만 이 방식으로 처리합니다. 그러나 이 방법은 런타임 시 타입 로드 오버헤드와 GC에서 문자열이 수집되지 않는 등의 영향이 있을 수 있습니다. 향후 작업으로 자동 임계값 결정, 단일 블롭 사용, IL 트리밍 친화적 방법 등이 논의되고 있습니다.

수정자가 있는 간단한 람다 매개변수

제안 내용은 람다 식에서 매개변수의 타입을 명시하지 않고도 수정자를 사용할 수 있게 하자는 것입니다. 예를 들어, 기존에는 (ref FileSystemEntry entry) =>와 같이 타입을 명시해야 했지만, 이제는 (ref entry) =>와 같이 타입 없이 수정자만으로 매개변수를 선언할 수 있습니다.

또한 다음과 같은 델리게이트가 있을 때:

delegate bool TryParse<T>(string text, out T result);

타입을 생략하고 수정자를 포함하여 다음과 같이 사용할 수 있습니다:

TryParse<int> parse1 = (text, out result) => Int32.TryParse(text, out result);

세부 설계에서는 문법 변경 없이 기존 문법을 활용합니다. 람다 매개변수 목록에서 타입 없이 수정자와 식별자를 사용하는 것을 허용하며, 이는 ref, out, in, ref readonly 등의 수정자를 포함합니다.

이 변경은 매개변수 타입의 유무와 관계없이 수정자의 사용을 일관되게 처리하며, 람다 식의 간결성과 가독성을 높입니다. 또한 scoped, params 등의 수정자도 타입 없이 사용할 수 있지만, 이 경우 오버로드 결정에는 영향을 주지 않습니다.

이 제안을 통해 개발자는 람다 식에서 불필요한 타입 명시를 줄이고, 필요한 수정자를 포함하여 더 간결하고 명확한 코드를 작성할 수 있습니다.

2개의 좋아요

partial 이벤트 및 생성자

이 제안은 C#에서 부분 메서드와 속성처럼 이벤트와 생성자에도 partial 수식어를 허용하여 선언과 구현을 분리할 수 있도록 하는 것입니다. 이를 통해 약한 이벤트 라이브러리나 인터롭 코드 생성 시나리오에서 유용하게 사용할 수 있습니다. 부분 이벤트와 생성자는 부분 타입의 멤버로만 선언될 수 있으며, 하나의 정의 선언과 하나의 구현 선언이 필요합니다. 시그니처는 일치해야 하며, 속성은 결합됩니다. 문서화 주석은 구현 선언의 주석만 사용됩니다. 이로써 C#의 유연성과 사용성을 높이고자 합니다.

1개의 좋아요

Extension 멤버

확장 선언은 비제네릭, 비중첩 정적 클래스 내에서 선언되며, 수신자 매개변수와 관련된 타입 매개변수 및 제약 조건을 포함합니다. 확장 멤버는 메서드, 속성, 인덱서, 연산자 등을 포함할 수 있으며, 수신자 매개변수를 통해 인스턴스 멤버에 접근합니다. 확장 선언은 메타데이터로 변환되어 정적 구현 메서드로 컴파일됩니다. 이 제안은 기존 확장 메서드와의 호환성을 유지하면서 새로운 기능을 추가하는 것을 목표로 합니다.

1개의 좋아요

Null-conditional assignment

Null-conditional assignment는 C#에서 null 조건부 접근 연산자(?.)를 사용하여 객체가 null이 아닐 때만 할당을 수행할 수 있게 하는 기능입니다. 이 기능은 속성과 Set() 메서드 간의 일관성을 제공하고, UI 코드에서 이벤트 핸들러를 연결하는 데 유용합니다. 주요 동작은 객체가 null이 아닐 때만 오른쪽 할당식이 평가된다는 것입니다. 복합 할당과 이벤트 핸들러 연결이 가능하지만, 증감 연산자나 구조 분해 할당에는 사용할 수 없습니다. 이 기능은 주로 참조 타입에 적용되며, 값 타입에는 제한적으로 적용됩니다. 이로 인해 IDE에서 추가적인 작업이 필요할 수 있습니다.

MyClass? myObject = null;

// Null-conditional assignment: myObject가 null이 아니면 Obj에 새 객체를 할당
myObject?.Obj = new object();

// 이벤트 핸들러를 null-conditional assignment로 추가
myObject?.MyEvent += () => Console.WriteLine("Event triggered!");

// myObject가 null이므로 아무 작업도 수행되지 않음
myObject?.TriggerEvent();

// myObject를 새 인스턴스로 초기화
myObject = new MyClass();

다시 null-conditional assignment: 이제 myObject가 null이 아니므로 Obj에 새 객체가 할당됨
myObject?.Obj = new object();

// 이벤트 핸들러를 추가
myObject?.MyEvent += () => Console.WriteLine("Event triggered!");

// 이벤트 트리거: "Event triggered!" 출력
myObject?.TriggerEvent();
2개의 좋아요

Ignored Directives

C# 언어에 스크립팅 기능을 강화하기 위해 새로운 전처리 지시문인 “해시뱅” 지시문을 도입하는 제안입니다. 이는 C# 파일의 최상위에 C# 문을 허용하고, .cs 파일을 직접 실행할 수 있는 기능을 염두에 둔 것입니다. 해시뱅 지시문은 #!<경로> 형식으로, 언어에서 무시되며 다음 줄로 넘어갑니다. 이로 인해 언어의 복잡성이 증가할 수 있으며, 스크립팅 지원 없이 파일에 나타날 경우 혼란을 초래할 수 있습니다. 해결되지 않은 질문으로는 다른 전처리 지시문과의 상호작용 방식이 있습니다.

1개의 좋아요

요약

.NET CLI에서 명시적인 프로젝트 파일 없이 C# 소스 파일을 실행할 수 있도록 하는 기능 확장을 제안합니다. 이를 통해 파일 기반 프로그램을 지원하며, 이는 PowerShell이나 bash 스크립트의 대안이 될 수 있고, 새로운 사용자에게 진입 장벽을 낮출 수 있습니다. 파일 기반 프로그램은 암시적인 프로젝트 파일을 가지며, 필요 시 명령어를 통해 실제 프로젝트 파일로 변환할 수 있습니다. 초기 구현은 단일 파일 지원에 제한되며, 최종적으로는 디렉토리 내 모든 파일을 포함할 계획입니다. 여러 엔트리 포인트를 지원하고, 프로젝트 메타데이터를 C# 지시문으로 지정할 수 있습니다. 이 기능은 단계적으로 구현될 예정입니다.

1개의 좋아요

이 기능을 통해 이전에는 메서드만 확장 가능했다면 이제는 속성, 인덱서, 연산자 등도 확장 가능하게 되었어요.

1개의 좋아요

Dictionary expressions

이제 Dictionary도 [“key”: value, ..] 형태로 초기화 할 수 있습니다.

C#의 Dictionary Expressions는 C# 12 Collection Expressions의 확장으로, [“key”: value, …]와 같은 간결한 문법으로 Dictionary를 쉽게 생성할 수 있게 해줍니다. 또한, [..otherDict, “key”: value]처럼 spread 연산자(..)로 다른 Dictionary나 KeyValuePair 컬렉션을 병합가능하며, Dictionary, ConcurrentDictionary, ImmutableDictionary 등의 다양한 타입에 적용됩니다.

이 문법의 목적은 기존 컬렉션/딕셔너리 초기화 코드의 장황함과 불편함을 해소하고, 다른 언어들(Python, Swift 등)처럼 쉽고 명확하게 Dictionary를 생성하도록 하는 것입니다. 또한, Dictionary 생성 시 비교자(comparer) 전달도 간단히 지원합니다.

[“key”: value] 구조는 KeyValuePair를 요소로 하는 리스트 등에도 사용할 수 있고, 값 변환, 타입 추론, 오버로드 결정을 위한 세부 규칙도 포함되어 있습니다. C# 14부터는 params와의 결합도 확장됩니다.

그밖에 다양한 구현, 타입 변환 및 규칙이 논의되어 있고, 비교자 전달 방식/문법 등에 대해 추가 논의가 진행 중입니다.

요약: Python 등에서 익숙한 [{key: value}]식 딕셔너리 리터럴, spread(병합), 간결한 비교자 지정, 다양한 딕셔너리 타입 지원 기능이 C#에 도입되는 새로운 Collection Expression 확장안입니다.

2개의 좋아요