챔피언 발행호: https://github.com/dotnet/csharplang/issues/8887
동기
사전 식 기능은 최종 컬렉션의 동작을 구성하기 위해 사용자 지정 데이터를 따라 컬렉션 식이 전달되어야 하는 필요성을 확인했습니다. 특히 사전을 사용하면 사용자가 키가 비교되는 방식을 사용자 지정할 수 있으며, 이를 사용하여 키 간의 같음을 정의하고 정렬 또는 해시를 지정할 수 있습니다(정렬 또는 해시된 컬렉션의 경우 각각). 이러한 필요성은 모든 종류의 사전 형식(예: D d = new D(...)D d = D.CreateRange(...) 짝수IDictionary<...> d = <synthesized dict>)을 만들 때 적용됩니다.
이를 지원하기 위해 새 with(...arguments...) 요소가 다음과 같이 컬렉션 식의 첫 번째 요소로 제안됩니다.
Dictionary<string, int> nameToAge = [with(comparer), .. d1, .. d2, .. d3];
- 호출
...arguments...로 변환할new CollectionType(...)때 적절한 생성자를 확인하는 데 사용되며 그에 따라 전달됩니다. - 호출로 변환할
CollectionFactory.Create때 요소...arguments...인수와 함께ReadOnlySpan<ElementType>이전에 전달되며, 모두 적절한Create오버로드를 확인하는 데 사용되며 그에 따라 전달됩니다. - 인터페이스로 변환할 때(예:
IDictionary<,>) 단일 인수만 허용됩니다. 잘 알려진 BCL 비교자 인터페이스 중 하나를 구현하고 최종 인스턴스의 의미 체계를 비교하는 키를 제어하는 데 사용됩니다.
이 구문은 다음과 같이 선택되었습니다.
- 모든 정보를 구문 내에
[...]유지합니다. 코드가 여전히 생성 중인 컬렉션을 명확하게 나타내는지 확인합니다. - 생성자를 호출
new하는 것을 의미하지는 않습니다(모든 컬렉션을 만드는 방법이 아닌 경우). - 컬렉션의 값을 여러 번 만들거나 복사하는 것을 의미하지는 않습니다(예: 후위
with { ... }수). - 특히 C#의 일관된 왼쪽에서 오른쪽 식 계산 순서 지정 의미 체계를 사용하여 작업의 순서를 왜곡하지 않습니다. 예를 들어 컬렉션을 채우는 데 사용되는 식을 평가한 후에 는 컬렉션을 생성하는 데 사용되는 인수를 평가하지 않습니다.
- 사용자가 (잠재적으로 큰) 컬렉션 식의 끝까지 읽어 핵심 동작 의미 체계를 결정하도록 강제하지 않습니다. 예를 들어 100줄 사전의 끝부분에 표시해야만 올바른 키 비교자를 사용하고 있음을 알 수 있습니다.
- 둘 다 미묘하지는 않지만 지나치게 자세한 내용은 아닙니다. 예를 들어 인수를 나타내는 대신
,사용하는;것은 놓칠 수 있는 매우 쉬운 구문입니다.with()6자만 추가하면 특히 키워드의 구문 색 지정으로 눈에 쉽게 띄게with됩니다. - 잘 읽습니다. "이 인수는 이러한 요소로 구성된 'with' 컬렉션 식입니다."
- 사전과 집합 모두에 대한 비교자의 필요성을 해결합니다.
- 인수 전달에 대한 사용자의 요구 또는 향후 비교자를 넘어서는 요구 사항이 이미 처리되었는지 확인합니다.
- 기존 코드와 충돌하지 않습니다(검색에 사용 https://grep.app/ ).
디자인 철학
아래 섹션에서는 이전 디자인 철학 토론에 대해 설명합니다. 특정 양식이 거부된 이유를 포함합니다.
이 사용자 정의 데이터를 제공하는 데 사용할 수 있는 두 가지 주요 방향이 있습니다. 첫 번째는 비교자 공간(BCL 또는 IEqualityComparer<T> 형식에서 상속되는 형식으로 정의됨)의 IComparer<T> 특수한 경우 값만을 지정하는 것입니다. 두 번째는 컬렉션 식을 만들 때 최종 호출된 API에 임의의 인수를 제공하는 일반화된 메커니즘을 제공하는 것입니다. 기본 사전 식 사양은 전자를 수행하는 방법을 보여 주지만 이 사양은 후자를 수행하려고 합니다.
비교자를 통과하기 위한 해결책을 검토한 결과, 임의 인수로 확장하려는 경우 접근 방식의 약점이 드러났습니다. 다음은 그 예입니다.
다음과
[StringComparer.OrdinalIgnoreCase, "mads": 21]같이 요소 구문을 다시 사용합니다. 이는 비교자가 공통 형식에서 상속되지 않는 공간에서KeyValuePair<,>잘 작동합니다. 그러나 그것은 하나가 할 수있는 세계에서 분해 :HashSet<object> h = [StringComparer.OrdinalIgnoreCase, "v"]. 비교자를 따라 전달되고 있나요? 또는 집합에 두 개의 개체 값을 넣으려고 합니까?인수와 요소를 미묘한 구문으로 구분합니다(예: 쉼표 대신 세미콜론을 사용하여 구분
[comparer; v1]). 이렇게 하면 사용자가 의도[1, 2]한 경우(두 요소가 있는 컬렉션) 실수로 '[1; 2]1'을 전달하는 컬렉션(예: 'capacity' 인수List<>를 가져오고 단일 값 '2'만 포함)하는 매우 혼란스러운 상황이 발생할 수 있습니다.
따라서 임의 인수를 지원하기 위해 이러한 값을 보다 명확하게 구분하기 위해 보다 명확한 구문이 필요하다고 생각합니다. 이 공간에는 몇 가지 다른 디자인 문제도 있습니다. 특정 순서가 없는 순서는 다음과 같습니다.
솔루션이 모호하지 않고 사람들이 현재 컬렉션 식과 함께 사용할 수 있는 코드로 인해 중단이 발생합니다. 다음은 그 예입니다.
List<Widget> c = [new(...), w1, w2, w3];이 식은 새 위젯을
new(...)만드는 '암시적 개체 생성'인 오늘날에도 유효합니다. 기존 코드를 확실히 중단하기 때문에 인수를 생성자에 전달하기 위해List<>이 인수를 용도로 변경할 수 없습니다.구문이 구문 외부
[...]로 확장되지 않습니다. 다음은 그 예입니다.HashSet<string> s = [...] with ...;이러한 구문은 컬렉션이 먼저 만들어지고 다른 형식으로 다시 만들어지므로 데이터의 여러 변환을 의미하고 잠재적으로 원치 않는 더 높은 비용(내보낸 것이 아니더라도)으로 해석될 수 있습니다.
이
new공간에서 사용할 수 있는 잠재적 키워드로는 바람직하지 않습니다.[...]둘 다 이미새 개체가 생성되었음을 나타내고 컬렉션 식의 변환이 생성자가 아닌 API(예: Create 메서드 패턴)를 통과할 수 있기 때문입니다.솔루션이 지나치게 자세한 정보가 아닙니다. 컬렉션 식의 핵심 가치 제안은 간결성입니다. 따라서 양식이 많은 양의 구문 스캐폴딩을 추가하는 경우 뒤로 한 걸음 뒤로 이동한 것처럼 느껴지며 컬렉션 식을 사용하는 값 제안을 기존 API로 호출하여 컬렉션을 만드는 것과 비교합니다.
같은 new([...], ...) 구문은 위의 '2'와 '3'을 모두 위반합니다. 생성자를 호출하는 것처럼 표시 되고 (그렇지 않을 수 있는 경우) 생성된 컬렉션 식이 해당 생성자에 전달됨을 의미합니다. 이는 확실히 그렇지 않습니다.
위의 모든 사항에 따라 컬렉션 식의 목표 범위를 벗어나지 않고 인수 전달의 요구를 해결하는 소수의 옵션이 등장했습니다.
[with(...arguments...)] 디자인
Syntax:
collection_element
: expression_element
| spread_element
+ | with_element
;
+with_element
+ : 'with' argument_list
+ ;
이 문법 프로덕션에서 즉시 도입된 구문 모호성이 있습니다. 간 spread_elementexpression_element 모호성과 유사합니다( 여기서 설명한 경우와 그 사이에 with_elementexpression_element는 즉각적인 구문 모호성이 있습니다.)
특히 with(<arguments>) 둘 다에 대한 with_element프로덕션 본문이며 을 통해 expression_element -> expression -> ... -> invocation_expression연결할 수도 있습니다. collection_elements 대한 간단한 중요 규칙이 있습니다. 특히, 요소가 어휘적으로 토큰 시퀀스 with( 로 시작하는 경우 항상 로 처리 with_element됩니다.
이것은 두 가지 방법으로 유익합니다. 먼저 컴파일러 구현은 구문 분석할 요소의 종류를 결정하기 위해 표시되는 바로 다음 토큰만 확인해야 합니다. 둘째, 그에 따라 사용자는 정신적으로 다음 항목을 구문 분석하여 이를 어떤 요소로 with_elementexpression_element생각해야 하는지 여부를 확인할 필요 없이 어떤 종류의 요소를 가지고 있는지 사소한 방식으로 이해할 수 있습니다.
예시
이 모양에 대한 예는 다음과 같습니다.
// With an existing type:
// Initialize to twice the capacity since we'll have to add
// more values later.
List<string> names = [with(capacity: values.Count * 2), .. values];
이러한 형태는 합리적으로 잘 "읽는"것 같습니다. 이러한 모든 경우에서 코드는 "컬렉션 식을 만들고, 마지막 인스턴스를 제어하기 위해 전달할 다음 인수를 'with'로 만든 다음, 이를 채우는 데 사용되는 후속 요소를 만듭니다. 예를 들어 첫 번째 줄은 "'with' 문자열 목록을 만들어 분산할 값의 2배에 달하는 용량을 만듭니다."
중요한 것은 이 코드는 다음과 같은 [arg; element]양식과 같이 간과될 가능성이 거의 없으며 최소한의 세부 정보 표시도 추가하고 원하는 인수를 전달할 수 있는 많은 유연성을 제공합니다.
이는 기술적으로 호환성이 손상되는 변경 with(...) 이 될 수with있습니다. 그러나 암시적으로 형식화된 값을 with(...) 만드는 알려진 권장 방법과는 달리 new(...) 메서드 이름처럼 메서드에 대한 .Net 명명의 위반을 실행할 가능성이 훨씬 적습니다. 사용자에게 이러한 메서드가 있는 경우는 드물기 때문에 해당 메서드를 사용하여 @with(...)기존 메서드를 계속 호출할 수 있습니다.
다음과 같이 이 with(...) 요소를 번역합니다.
List<string> names = [with(/*capacity*/10), ...]; // translates to:
// argument_list *becomes* the argument list for the
// constructor call.
__result = new List<string>(10); // followed by normal initialization
// or
IList<string> names2 = [with(capacity: 20), ...]; // translates to:
__result = new List<string>(20);
즉, argument_list 인수는 생성자를 호출하는 경우 적절한 생성자에 전달되거나 해당 메서드를 호출하는 경우 적절한 'create 메서드'에 전달됩니다. 또한 대상 사전 인터페이스 형식 중 하나를 인스턴스화하여 동작을 제어할 때 BCL 비교자 형식에서 상속되는 단일 인수를 제공할 수 있습니다.
Conversions
컬렉션 식 의 변환 섹션은 다음과 같이 업데이트됩니다.
> A struct or class type that implements System.Collections.IEnumerable where:
- * The type has an applicable constructor that can be invoked with no arguments, and the constructor is accessible at the location of the collection expression.
+ a. the collection expression has no `with_element` and the type has an applicable constructor
+ that can be invoked with no arguments, accessible at the location of the collection expression. or
+ b. the collection expression has a `with_element` and the type has at least one constructor
+ accessible at the location of the collection expression.
변환이 존재하는지 여부는 해당 인수 내 argument_list 의 with_element 실제 인수에 영향을 미치지 않습니다. 그 자체의 with_element 존재 또는 부재. 여기서 직관은 컬렉션 식이 하나(예: [x, y, z])없이 작성된 경우 인수 없이 생성자를 호출할 수 있어야 한다는 것입니다. 있는 경우 [with(...), x, y, z] 적절한 생성자를 호출할 수 있습니다. 이는 인수 없는 생성자로 호출할 수 없는 형식을 컬렉션 식과 함께 사용할 수 있지만 컬렉션 식이 포함된 경우에만 사용할 수 있음을 의미합니다.with_element
건설에 미치는 영향에 대한 with_element 실제 결정은 다음과 같습니다.
건축
생성은 다음과 같이 업데이트됩니다.
컬렉션 식의 요소는 왼쪽에서 오른쪽으로 순서대로 계산됩니다. 컬렉션 인수 내에서 인수는 왼쪽에서 오른쪽으로 순서대로 평가됩니다. 각 요소 또는 인수는 정확히 한 번 평가되며, 추가 참조는 이 초기 평가의 결과를 참조합니다.
collection_arguments 포함되어 있고 컬렉션 식의 첫 번째 요소가 아닌 경우 컴파일 시간 오류가 보고됩니다.
인수 목록에동적 형식의 값이 포함된 경우 컴파일 시간 오류가 보고됩니다(LDM-2025-01-22).
생성자
대상 형식이 구현System.Collections.IEnumerable하는 구조체 또는 클래스 형식이고 대상 형식에 create 메서드가 없으며 대상 형식이 제네릭 매개 변수 형식이 아닌 경우 다음을 수행합니다.
- 오버로드 확인 은 후보로부터 최상의 인스턴스 생성자를 결정하는 데 사용됩니다.
- 후보 생성자 집합은 적용 가능한 함수 멤버에 정의된 인수 목록과 관련하여 적용 가능한 대상 형식에 선언된 모든 액세스 가능한 인스턴스 생성자입니다.
- 최상의 인스턴스 생성자를 찾은 경우 인수 목록을 사용하여 생성자가 호출됩니다.
- 생성자에 매개 변수가 있는
params경우 호출이 확장된 형식일 수 있습니다.
- 생성자에 매개 변수가 있는
- 그렇지 않으면 바인딩 오류가 보고됩니다.
// List<T> candidates:
// List<T>()
// List<T>(IEnumerable<T> collection)
// List<T>(int capacity)
List<int> l;
l = [with(capacity: 3), 1, 2]; // new List<int>(capacity: 3)
l = [with([1, 2]), 3]; // new List<int>(IEnumerable<int> collection)
l = [with(default)]; // error: ambiguous constructor
CollectionBuilderAttribute 메서드
대상 형식이 create 메서드를 사용하는 형식인 경우 다음을 수행합니다.
- 오버로드 확인 은 후보로부터 최상의 만들기 방법을 결정하는 데 사용됩니다.
- 대상 형식에 대한 각 create 메서드에 대해 마지막 매개 변수 없이 create 메서드와 동일한 시그니처를 사용하여 프로젝션 메서드를 정의합니다.
- 후보 프로젝션 메서드 집합은 해당 함수 멤버에 정의된 인수 목록과 관련하여 적용할 수 있는 프로젝션 메서드입니다.
- 최상의 프로젝션 메서드가 발견되면 해당 create 메서드는 요소가 포함된 인수 목록이 추가된 상태에서
ReadOnlySpan<T>호출됩니다. - 그렇지 않으면 바인딩 오류가 보고됩니다.
[CollectionBuilder(typeof(MyBuilder), "Create")]
class MyCollection<T> { ... }
class MyBuilder
{
public static MyCollection<T> Create<T>(ReadOnlySpan<T> elements);
public static MyCollection<T> Create<T>(IEqualityComparer<T> comparer, ReadOnlySpan<T> elements);
}
MyCollection<string> c1 = [with(GetComparer()), "1", "2"];
// IEqualityComparer<string> _tmp1 = GetComparer();
// ReadOnlySpan<string> _tmp2 = ["1", "2"];
// c1 = MyBuilder.Create<string>(_tmp1, _tmp2);
MyCollection<string> c2 = [with(), "1", "2"];
// ReadOnlySpan<string> _tmp3 = ["1", "2"];
// c2 = MyBuilder.Create<string>(_tmp3);
CollectionBuilderAttribute: 메서드 만들기
대상 형식 정의[CollectionBuilder] 에 특성이 있는 컬렉션 식의 경우 create 메서드는 컬렉션 식에서 업데이트된 create 메서드입니다.
특성은
[CollectionBuilder(...)]컬렉션 형식의 인스턴스를 생성하기 위해 호출할 메서드의 작성기 형식 및 메서드 이름을 지정합니다.작성기 형식은 제네릭
class이 아닌 형식이거나struct.먼저 적용 가능한 만들기 메서드
CM집합이 결정됩니다. 다음 요구 사항을 충족하는 메서드로 구성됩니다.
- 메서드는 특성에
[CollectionBuilder(...)]지정된 이름이 있어야 합니다.- 작성기 형식 에서 직접 메서드를 정의해야 합니다.
- 메서드는 .이어야
static합니다.- 컬렉션 식이 사용되는 위치에 메서드에 액세스할 수 있어야 합니다.
- 메서드의 arity 는 컬렉션 형식의 결과 와 일치해야 합니다.
- 메서드에는 값으로 전달되는 형식
System.ReadOnlySpan<E>의 마지막 매개 변수가 있어야 합니다.- 메서드 반환 형식에서 컬렉션 형식으로의 ID 변환, 암시적 참조 변환 또는 boxing 변환이 있습니다.
기본 형식 또는 인터페이스에 선언된 메서드는 무시되며 집합의
CM일부가 아닙니다.
형식 선언
C<S0, S1, …>에 연결된 작성기 메서드C<T0, T1, …>가 있는 대상 형식 이 있는B.M<U0, U1, …>()의 경우 대상 형식의 제네릭 형식 인수가 순서대로 적용되고 가장 바깥쪽에 포함된 형식에서 가장 안쪽에 있는 제네릭 형식 인수가 작성기 메서드에 적용됩니다.
이전 알고리즘의 주요 차이점은 다음과 같습니다.
- 메서드 만들기에는 매개 변수 앞에 추가 매개 변수가
ReadOnlySpan<E>있을 수 있습니다. - 여러 만들기 메서드가 지원됩니다.
인터페이스 대상 형식
대상 형식이 인터페이스 형식인 경우 다음을 수행합니다.
오버로드 확인 은 최상의 후보 메서드 서명을 결정하는 데 사용됩니다.
후보 서명 집합은 해당 함수 멤버에 정의된 인수 목록과 관련하여 적용할 수 있는 대상 인터페이스에 대한 아래 서명입니다.
Interfaces 후보 서명 IEnumerable<E>IReadOnlyCollection<E>IReadOnlyList<E>()(매개 변수 없음)ICollection<E>IList<E>List<E>()List<E>(int)
최상의 메서드 시그니처가 발견되면 의미 체계는 다음과 같습니다.
- 에 대한
IEnumerable<E>IReadOnlyCollection<E>IReadOnlyList<E>후보 서명이며, 단순히()요소를 전혀 포함하지with()않는 것과 동일한 의미를 갖습니다. - 후보 서명 및
IList<T>ICollection<T>생성자의 서명List<T>()List<T>(int)입니다. 값을 생성할 때( 변경 가능한 인터페이스 변환 참조) 해당List<T>생성자가 호출됩니다. - 그렇지 않으면 바인딩 오류가 보고됩니다.
Dictionary-Interface 대상 유형
여기서는 에 정의된 기능의 일부로 지정됩니다 https://github.com/dotnet/csharplang/blob/main/proposals/dictionary-expressions.md.
위의 목록은 다음 항목을 포함하도록 보강됩니다.
| Interfaces | 후보 서명 |
|---|---|
IReadOnlyDictionary<K, V> |
() (매개 변수 없음)(IEqualityComparer<K>? comparer) |
IDictionary<K, V> |
Dictionary<K, V>()Dictionary<K, V>(int)Dictionary<K, V>(IEqualityComparer<K>)Dictionary<K, V>(int, IEqualityComparer<K>) |
최상의 메서드 시그니처가 발견되면 의미 체계는 다음과 같습니다.
- 후보 서명
IReadOnlyDictionary<K, V>()은 (요소가 전혀 없는with()것과 동일한 의미를 갖습니다)입니다(IEqualityComparer<K>). 이 비교자는 컴파일러가 만들도록 선택한 대상 사전의 키를 적절하게 해시하고 비교하는 데 사용됩니다( 변경할 수 없는 인터페이스 변환 참조). - 후보 서명은
IDictionary<T>,Dictionary<K, V>(int)Dictionary<K, V>(IEqualityComparer<K>)및Dictionary<K, V>(int, IEqualityComparer<K>)생성자의 서명Dictionary<K, V>()입니다. 값을 생성할 때( 변경 가능한 인터페이스 변환 참조) 해당Dictionary<K, V>생성자가 호출됩니다. - 그렇지 않으면 바인딩 오류가 보고됩니다.
IDictionary<string, int> d;
IReadOnlyDictionary<string, int> r;
d = [with(StringComparer.Ordinal)]; // new Dictionary<string, int>(StringComparer.Ordinal)
r = [with(StringComparer.Ordinal)]; // new $PrivateImpl<string, int>(StringComparer.Ordinal)
d = [with(capacity: 2)]; // new Dictionary<string, int>(capacity: 2)
r = [with(capacity: 2)]; // error: 'capacity' parameter not recognized
d = [with()]; // Legal: empty arguments supported for interfaces
기타 대상 형식
대상 형식이 다른 형식인 경우 비어 있더라도 인수 목록에 바인딩 오류가 보고됩니다.
Span<int> a = [with(), 1, 2, 3]; // error: arguments not supported
Span<int> b = [with([1, 2]), 3]; // error: arguments not supported
int[] a = [with(), 1, 2, 3]; // error: arguments not supported
int[] b = [with(length: 1), 3]; // error: arguments not supported
참조 안전성
요소를 고려 with() 하도록 collection-expressions.md#ref-safety 규칙을 조정합니다.
또한 §16.4.15 안전한 컨텍스트 제약 조건을 참조하세요.
메서드 만들기
이 섹션은 CollectionBuilderAttribute 메서드에 정의된 제약 조건을 충족하는 대상 형식의 컬렉션 식에 적용됩니다.
safe-context는 collection-expressions.md#ref-safety(굵게 변경)에서 절을 수정하여 결정됩니다.
- 대상 형식이 create 메서드를 사용하는 ref 구조체 형식인 경우 컬렉션 식의 안전 컨텍스트는 인수
with()가 요소 인수이고 컬렉션 식이 마지막 매개 변수(ReadOnlySpan<E>매개 변수)의 인수인 create 메서드 호출의 안전 컨텍스트입니다.
메서드 인수는 컬렉션 식에 적용되는 제약 조건과 일치해야 합니다 . 위의 안전 컨텍스트 결정과 마찬가지로 메서드 인수는 컬렉션 식을 create 메서드의 호출로 처리하여 제약 조건을 일치시켜야 합니다 . 여기서 인수는 with() 요소 인수이고 컬렉션 식은 마지막 매개 변수의 인수입니다.
생성자 호출
이 섹션은 생성자에 정의된 제약 조건을 충족하는 대상 형식의 컬렉션 식에 적용됩니다.
다음 폼의 ref 구조체 형식 컬렉션 식의 경우:
[with(a₁, a₂, ..., aₙ), e₁, e₂, ..., eₙ]
컬렉션 식의 안전 컨텍스트 는 다음 식의 안전 컨텍스트 중 가장 좁은 컨텍스트입니다.
- 대상 형식이 있는
C개체 만들기 식new C(a₁, a₂, ..., aₙ) - 요소 식
e₁, e₂, ..., eₙ(식 자체 또는 스프레드 요소의 경우의 분산 값)입니다.
메서드 인수는 컬렉션 식에 적용되는 제약 조건과 일치해야 합니다 . 제약 조건은 컬렉션 식을 하위 수준 struct-improvements.md#rules-for-object-initializer당 폼 new C(a₁, a₂, ..., aₙ) { e₁, e₂, ..., eₙ } 의 개체 생성으로 처리하여 적용됩니다.
- 식 요소는 컬렉션 요소 이니셜라이저인 것처럼 처리됩니다.
- 스프레드 요소는 메서드가 있는
SpreadType경우 스프레드 값의 형식을CAdd(SpreadType spread)일시적으로 가정하여 유사하게 처리됩니다.
답변된 질문
dynamic 인수
형식의 인수를 dynamic 허용해야 하나요? 오버로드 확인에 런타임 바인더를 사용해야 할 수 있으므로 컬렉션 작성기 사례와 같이 후보 집합을 제한하기가 어려울 수 있습니다.
해상도: 허용. LDM-2025-01-22
with() 호환성이 손상되는 변경
제안된 with() 요소는 호환성이 손상되는 변경입니다.
object x, y, z = ...;
object[] items = [with(x, y), z]; // C#13: ok; C#14: error args not supported for object[]
object with(object x, object y) { ... }
호환성이 손상되는 변경이 허용되는지, 호환성이 손상되는 변경이 언어 버전에 연결되어야 하는지 여부를 확인합니다.
해상도: 이전 언어 버전으로 컴파일할 때 이전 동작(호환성이 손상되는 변경 없음)을 유지합니다. LDM-2025-03-17
인수가 컬렉션 식 변환에 영향을 주나요?
컬렉션 인수와 해당 메서드가 컬렉션 식의 변환성에 영향을 주어야 하나요?
Print([with(comparer: null), 1, 2, 3]); // ambiguous or Print<int>(HashSet<int>)?
static void Print<T>(List<T> list) { ... }
static void Print<T>(HashSet<T> set) { ... }
인수가 적용 가능한 메서드에 따라 변환성에 영향을 미치는 경우 인수는 형식 유추에도 영향을 줄 수 있습니다.
Print([with(comparer: StringComparer.Ordinal)]); // Print<string>(HashSet<string>)?
참조의 경우 대상 형식 new() 의 유사한 경우 오류가 발생합니다.
Print<int>(new(comparer: null)); // error: ambiguous
Print(new(comparer: StringComparer.Ordinal)); // error: type arguments cannot be inferred
해상도: 컬렉션 인수는 변환 및 형식 유추에서 무시해야 합니다. LDM-2025-03-17
컬렉션 작성기 메서드 매개 변수 순서
컬렉션 작성기 메서드의 경우 범위 매개 변수는 컬렉션 인수에 대한 매개 변수 앞이나 뒤여야 하나요?
요소를 먼저 사용하면 인수를 선택 사항으로 선언할 수 있습니다.
class MySetBuilder
{
public static MySet<T> Create<T>(ReadOnlySpan<T> items, IEqualityComparer<T> comparer = null) { ... }
}
먼저 인수를 사용하면 확장된 형식으로 params 직접 호출을 지원하기 위해 범위가 매개 변수가 될 수 있습니다.
var s = MySetBuilder.Create(StringComparer.Ordinal, x, y, z);
class MySetBuilder
{
public static MySet<T> Create<T>(IEqualityComparer<T> comparer, params ReadOnlySpan<T> items) { ... }
}
해상도: 요소의 범위 매개 변수는 마지막 매개 변수여야 합니다. LDM-2025-03-12
이전 언어 버전의 인수
이전 언어 버전으로 컴파일할 때 오류가 보고 with() 되거나 범위의 다른 기호에 바인딩되나요 with ?
해상도: 이전 언어 버전으로 컴파일할 때 컬렉션 식 내에서 호환성이 손상되는 변경 with 이 없습니다.
LDM-2025-03-17
인수가 필요한 대상 형식
모든 생성자 또는 팩터리 메서드에 하나 이상의 인수가 필요하기 때문에 인수를 제공해야 하는 대상 형식에 컬렉션 식 변환이 지원되어야 하나요?
이러한 형식은 명시적 with() 인수를 포함하는 컬렉션 식과 함께 사용할 수 있지만 매개 변수에는 형식을 params 사용할 수 없습니다.
예를 들어 팩터리 메서드에서 생성된 다음 형식을 고려합니다.
MyCollection<object> c;
c = []; // error: no arguments
c = [with(capacity: 1)]; // ok
[CollectionBuilder(typeof(MyBuilder), "Create")]
class MyCollection<T> : IEnumerable<T> { ... }
class MyBuilder
{
public static MyCollection<T> Create<T>(ReadOnlySpan<T> items, int capacity) { ... }
}
아래 예제와 같이 생성자가 직접 호출되는 경우에도 동일한 질문이 적용됩니다.
그러나 생성자가 직접 호출되는 대상 형식의 경우 컬렉션 식 변환 에는 현재 인수 없이 호출할 수 있는 생성자가 필요하지만 변환을 결정할 때 컬렉션 인수 는 무시됩니다.
c = []; // error: no arguments
c = [with(capacity: 1)]; // error: no constructor callable with no arguments?
class MyCollection<T> : IEnumerable<T>
{
public MyCollection(int capacity) { ... }
public void Add(T t) { ... }
// ...
}
해상도: 모든 생성자 또는 팩터리 메서드에 인수가 필요하고 변환이 필요한 with() 대상 형식으로의 변환을 지원합니다.
LDM-2025-03-05
__arglist
요소에서 with() 지원되어야 하나 __arglist 요?
class MyCollection : IEnumerable
{
public MyCollection(__arglist) { ... }
public void Add(object o) { }
}
MyCollection c;
c = [with(__arglist())]; // ok
c = [with(__arglist(x, y)]; // ok
해상도: 무료가 아니면 컬렉션 인수에서 지원 __arglist 되지 않습니다.
LDM-2025-03-05
인터페이스 형식에 대한 인수
인터페이스 대상 형식에 대해 인수를 지원해야 하나요?
ICollection<int> c = [with(capacity: 4)];
IReadOnlyDictionary<string, int> d = [with(comparer: StringComparer.Ordinal), ..values];
변경 가능한 인터페이스 형식의 경우 옵션은 다음과 같습니다.
- 인스턴스화에 필요한 잘 알려진 형식에서 액세스 가능한 생성자를 사용합니다.
List<T>또는Dictionary<K, V>. - 특정 형식에 관계없이 서명을 사용합니다(예:
new()new(int capacity)각 인터페이스에 대한ICollection<T>IList<T>잠재적 서명의 경우 생성 참조).
잘 알려진 형식에서 액세스 가능한 생성자를 사용하는 경우 다음과 같은 의미가 있습니다.
- 매개 변수 이름(optional-ness
params)은 매개 변수에서 직접 가져옵니다. - 허용되는 것과 같은
List(IEnumerable<T>)IList<int> list = [with(1, 2, 3)];컬렉션 식에는 유용하지 않을 수 있지만 액세스 가능한 모든 생성자가 포함됩니다. - 생성자 집합은 BCL 버전에 따라 달라질 수 있습니다.
다시 주석 처리: 잘 알려진 형식에서 액세스 가능한 생성자를 사용합니다. 우리는 이러한 형식을 사용할 것이라고 보장했기 때문에 이것은 단지 '떨어지기'만하며 이러한 값을 생성하는 가장 명확하고 간단한 경로입니다.
변경할 수 없는 인터페이스 형식의 경우 옵션은 다음과 유사합니다.
- 아무 작업도 수행하지 않습니다. 이
- C#14용 시나리오만 있을 수
new(IEqualityComparer<K> comparer)IReadOnlyDictionary<K, V>있지만 특정 형식에 관계없이 서명을 사용합니다.
일부 잘 알려진 형식(변경 가능한 인터페이스 형식에 대한 전략)에서 액세스 가능한 생성자를 사용하는 것은 특정 기존 형식과 관계가 없고 최종 형식을 사용하거나 합성할 수 있기 때문에 실행 가능하지 않습니다. 따라서 컴파일러가 해당 형식의 기존 생성자를 실제로 생성하는 변경 불가능한 인스턴스로 매핑할 수 있어야 하는 이상한 새 요구 사항이 있어야 합니다.
다시 주석 처리: 특정 형식에 관계없이 서명을 사용합니다. 또한 C# 14의 경우 사용자가 이를 제공할 수 있도록 유용성/의미 체계에 매우 중요하다고 생각하는 유일한 변경할 수 없는 인터페이스만 지원 new(IEqualityComparer<K> comparer)IReadOnlyDictionary<K, V> 합니다. 향후 C# 릴리스에서는 제공된 확실한 근거에 따라 이 집합을 확장하는 것을 고려할 수 있습니다.
해상도:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-04-23.md
인수는 인터페이스 대상 형식에 대해 지원됩니다. 변경 가능한 인터페이스와 변경할 수 없는 인터페이스 모두에 대해 인수 집합이 큐레이팅됩니다.
필요한 목록(여전히 LDM 비준 필요)은 인터페이스 대상 형식입니다.
빈 인수 목록
일부 또는 모든 대상 형식에 대해 빈 인수 목록을 허용해야 하나요?
빈 with() 항목은 아니요 with()와 동일합니다. 비어 있지 않은 경우와 일관성을 제공할 수 있지만 새 기능은 추가하지 않습니다.
List<int> l = [with()]; // ok? new List<int>()
ImmutableArray<int> m = [with()]; // ok? ImmutableArray.Create<int>()
IList<int> i = [with()]; // ok? new List<int>() or equivalent
IEnumerable<int> e = [with()]; // ok?
int[] a = [with()]; // ok?
Span<int> s = [with()]; // ok?
해상도:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-05-12.md#empty-argument-lists
인수 없이 호출할 수 있는 생성자 형식 및 작성기 형식에 대해 with()를 허용하고 인터페이스(변경 가능 및 읽기 전용) 형식에 대한 빈 생성자 서명을 추가합니다. 배열 및 범위는 해당 배열에 맞는 서명이 없기 때문에 with()를 허용하지 않습니다.
질문 열기
다음에서 열린 관심사를 마무리합니다. https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-03-17.md#conclusion
with(...) 는 을 사용하여 언어 [with(...)]의 호환성이 손상되는 변경입니다. 이 기능 이전에는 -invocation-expression을 호출 with한 결과인 하나의 요소가 있는 컬렉션 식을 의미합니다. 이 기능 후에는 인수가 전달된 컬렉션입니다.
사용자가 특정 언어 버전(예: C#-14/15?)을 선택하는 경우에만 이 중단이 발생하도록 하시겠습니까? 즉, 이전 구문 분석 중이면 이전 구문 분석 논리를 얻을 수 있지만 최신 버전에서는 최신 구문 분석 논리를 가져옵니다. 또는 이전 구문 분석에서도 항상 최신 구문 분석 논리를 사용하길 원합니까?
우리는 두 전략에 대한 사전 예술을 가지고있다.
required예를 들어, 항상 구문 분석에 관계 없이 새 논리로 구문 분석 됩니다. 반면, record/field 다른 사용자는 언어 버전에 따라 구문 분석 논리를 만듭니다.
마지막으로, 이는 KVP 요소의 구문을 소개하는 겹치고 영향을 Dictionary Expressions줍니다 key:value . 우리는 어떤 lang 버전에 대해, 그리고 그 자체로, 그리고 같은 [with(...) : expr] 것들에 대해 [with(...)] 우리가 원하는 행동을 설정하려고합니다.[expr : with(...)]
C# feature specifications