메모
이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.
기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련LDM(언어 디자인 모임) 노트에서 캡처됩니다.
사양문서에서 기능 명세를 C# 언어 표준으로 채택하는 프로세스에 대해 자세히 알아볼 수 있습니다.
챔피언 이슈: https://github.com/dotnet/csharplang/issues/8374
요약
params보다 일관되고 현재의 모호성 시나리오를 더 잘 처리하도록 더 나은 변환 규칙으로 업데이트합니다. 예를 들어 ReadOnlySpan<string> 및 ReadOnlySpan<object> 현재 [""]대한 오버로드 확인 중에 모호성을 일으킬 수 있습니다.
상세 디자인
다음은 식 규칙에서 더 나은 변환입니다. 이것들은 https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution규칙을 대체합니다.
이러한 규칙은 다음과 같습니다.
주어진 암시적 변환
C₁이(가) 식E을(를) 형식T₁으로(로) 변환하고, 암시적 변환C₂이(가) 식E을(를) 형식T₂으로(로) 변환할 때,C₁은 다음 중 하나가 성립하는 경우, 보다C₂입니다.
E컬렉션 식이며C₁식더 나은 컬렉션 변환입니다C₂E는 컬렉션 식이 아니며, 다음 중 하나가 성립합니다:
E정확히T₁일치하며ET₂정확히 일치하지 않습니다.E이T₁및T₂와 정확히 일치하거나 둘 모두와 일치하지 않으며,T₁은 더 나은 변환 대상T₂이다.E메서드 그룹입니다.
우리는 다음과 같이 표현식에서더 나은 컬렉션 변환을 위해 새로운 정의를 추가합니다.
주어진:
-
E은[EL₁, EL₂, ..., ELₙ]요소 식을 포함한 컬렉션 식입니다. -
T₁및T₂컬렉션 형식입니다. -
E₁T₁요소 형식입니다. -
E₂T₂요소 형식입니다. -
CE₁ᵢ은ELᵢ에서E₁로의 변환 일련입니다. -
CE₂ᵢ은ELᵢ에서E₂로의 변환 일련입니다.
E₁에서 E₂으로의 항등 변환이 있는 경우, 요소 변환은 서로 동등하게 좋습니다. 그렇지 않으면 E₁ 요소 변환이 다음과 같은 경우 E₂더 좋습니다.
- 각
ELᵢ에 대해CE₁ᵢ은 적어도CE₂ᵢ만큼 좋습니다. -
CE₁ᵢCE₂ᵢ보다 더 나은 하나 이상의 i가 있습니다. 그렇지 않으면 요소 변환 집합이 다른 집합보다 낫지 않으며 서로만큼 좋지도 않습니다.
ELᵢ스프레드 요소가 아닌 경우 식에서 더 나은 변환을 사용하여 변환 비교가 이루어집니다. 만약ELᵢ가 스프레드 요소라면, 우리는 스프레드 컬렉션의 요소 유형에서E₁또는E₂로 각각 더 나은 변환을 사용합니다.
C₁ 식C₂더 나은 컬렉션 변환입니다.
-
T₁및T₂모두 범위 형식이 아니며T₁암시적으로T₂변환할 수 있으며T₂암시적으로T₁변환할 수 없습니다. -
E₁E₂대한 ID 변환이 없으며E₁요소 변환이E₂더 낫습니다. -
E₁는E₂로의 항등 변환을 가지며, 다음 중 하나를 충족합니다.-
T₁은System.ReadOnlySpan<E₁>이고,T₂는System.Span<E₂>또는 -
T₁는System.ReadOnlySpan<E₁>또는System.Span<E₁>이며,T₂는 array_or_array_interface로, 요소 형식은E₂입니다.
-
그렇지 않으면 컬렉션 형식이 더 낫지 않으며 결과가 모호합니다.
메모
이러한 규칙은 컬렉션 형식 간의 변환 없이 다른 요소 형식을 사용하는 오버로드를 노출하는 메서드가 빈 컬렉션 식에 대해 모호하다는 것을 의미합니다. 예를 들면 다음과 같습니다.
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
시나리오:
일반 영어에서는 컬렉션 형식 자체가 동일해야 합니다. 또는 명확하게 더 나은 (즉, List<T> 및 List<T> 동일, List<T>IEnumerable<T>보다 명확하게 더 나은, List<T> 및 HashSet<T> 비교할 수 없습니다), 더 나은 컬렉션 형식에 대 한 요소 변환도 동일 하거나 더 나은 (즉, ReadOnlySpan<object>대 한 Span<string> 및 [""] 중에서 결정할 수 없습니다., 사용자가 해당 결정을 내려야 합니다). 이에 대한 더 많은 예는 다음과 같습니다.
T₁ |
T₂ |
E |
C₁ 변환 |
C₂ 변환 |
CE₁ᵢ 및 CE₂ᵢ |
결과 |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ 더 좋습니다. |
List<int> 선택됨 |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
해당 없음 |
T₂ 적용할 수 없음 |
List<int> 선택됨 |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
둘 다 더 낫지 않다 | 모호한 |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ 더 좋습니다. |
List<byte> 선택됨 |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
둘 다 더 낫지 않다 | 모호한 |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ 더 좋습니다. |
List<int?> 선택됨 |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ 더 좋습니다. |
List<short> 선택됨 |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ 더 좋습니다. |
IEnumerable<int> 선택됨 |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ 더 좋습니다. |
List<byte> 선택됨 |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ 더 좋습니다. |
int[] 선택됨 |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ 더 좋습니다. |
ReadOnlySpan<string> 선택됨 |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
해당 없음 | [Implicit Reference, Identity] |
T₁ 적용할 수 없음 |
ReadOnlySpan<object> 선택됨 |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ 더 좋습니다. |
Span<string> 선택됨 |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
해당 없음 |
T₁ 적용할 수 없음 |
ReadOnlySpan<object> 선택됨 |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ 더 좋습니다. |
ReadOnlySpan<InterpolatedStringHandler> 선택됨 |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - 그러나 상수 |
CE₂ᵢ 더 좋습니다. |
ReadOnlySpan<string> 선택됨 |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ 더 좋습니다. |
ReadOnlySpan<string> 선택됨 |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
해당 없음 | [Interpolated String, Identity] |
T₁ 적용할 수 없음 |
ReadOnlySpan<FormattableString> 선택됨 |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ 더 좋습니다. |
HashSet<short> 선택됨 |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ 더 좋습니다. |
Span<short> 선택됨 |
질문 열기
다른 형식보다 ReadOnlySpan/Span 얼마나 우선 순위를 지정해야 하나요?
현재 지정한 대로 다음 오버로드는 모호합니다.
C.M1(["Hello world"]); // Ambiguous, no tiebreak between ROS and List
C.M2(["Hello world"]); // Ambiguous, no tiebreak between Span and List
C.M3(["Hello world"]); // Ambiguous, no tiebreak between ROS and MyList.
C.M4(["Hello", "Hello"]); // Ambiguous, no tiebreak between ROS and HashSet. Created collections have different contents
class C
{
public static void M1(ReadOnlySpan<string> ros) {}
public static void M1(List<string> list) {}
public static void M2(Span<string> ros) {}
public static void M2(List<string> list) {}
public static void M3(ReadOnlySpan<string> ros) {}
public static void M3(MyList<string> list) {}
public static void M4(ReadOnlySpan<string> ros) {}
public static void M4(HashSet<string> hashset) {}
}
class MyList<T> : List<T> {}
여기까지 얼마나 가고 싶습니까?
List<T> 변형은 합리적으로 보이며 List<T> 하위 형식은 많이 존재합니다. 그러나 HashSet 버전은 매우 다른 의미 체계를 가지고 있으며, 이 API에서 ReadOnlySpan 것보다 실제로 "더 나쁘다"고 확신합니까?
C# feature specifications