注意
本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。
功能規格與已完成實作之間可能有一些差異。 這些差異是在的相關
冠軍問題: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是由元素表達式[EL₁, EL₂, ..., ELₙ]組成的集合表達式 -
T₁和T₂是集合類型 -
E₁是T₁的元素類型 -
E₂是T₂的元素類型 -
CE₁ᵢ是將ELᵢ轉換為E₁的系列 -
CE₂ᵢ是將ELᵢ轉換為E₂的系列
如果有從 E₁ 轉換成 E₂的身分識別轉換,則元素轉換會和彼此一樣好。 否則,轉換成
- 對於每個
ELᵢ,CE₁ᵢ至少和CE₂ᵢ一樣好。 - 至少存在一個條件 i,使得
CE₁ᵢ比CE₂ᵢ更好。否則,兩組元素轉換彼此無優劣,且其效果也不彼此相同。
如果ELᵢ不是展開元素,則使用較佳的轉換來比較表達式。 如果ELᵢ是散佈元素,我們會使用更佳的轉換方式,將散佈集合的元素類型分別轉換成E₁或E₂。
C₁ 是比 更好的集合轉換,若 :
-
T₁和T₂不是 範圍類型,而且T₁可以隱含轉換成T₂,但T₂無法隱含轉換成T₁或 沒有 的身分識別轉換,而 的項目轉換比 轉換成 的專案 更好, 或 -
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 更糟?