メモ
この記事は機能仕様についてです。 仕様は、機能の設計ドキュメントとして使用できます。 これには、提案された仕様の変更および機能の設計と開発時に必要な情報が含まれます。 これらの記事は、提案された仕様の変更が決定され、現在の 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 のルールを置き換えます。
次のような規則です。
E式から型T₁に変換する暗黙的な変換C₁と、E式から型T₂に変換する暗黙的な変換C₂を考えると、 次のいずれかが保持されている場合、C₁はC₂よりも 優れた変換 になります。
Eはコレクション式であり、C₁は より優れたC₂Eはコレクション式ではなく、次のいずれかを保持します。
EがT₁と完全に一致し、EがT₂と完全に一致しませんEはT₁およびT₂の両方またはどちらにも完全に一致し、T₁は よりもT₂ですEはメソッド グループです...
次のように、式からのコレクション変換を改善するための新しい定義を追加します。
たとえば、以下のように指定したとします。
-
Eは要素式が[EL₁, EL₂, ..., ELₙ]のコレクション式です -
T₁とT₂はコレクション型です -
E₁はT₁の要素型です -
E₂はT₂の要素型です -
CE₁ᵢはELᵢからE₁への一連の変換です -
CE₂ᵢはELᵢからE₂への一連の変換です
E₁ から E₂ への ID 変換がある場合、要素の変換は互いに同じになります。 それ以外の場合、E₁ への要素変換は、次の場合に E₂ への要素変換よりも優れています。
- すべての
ELᵢについて、CE₁ᵢは少なくともCE₂ᵢと同じくらい優れています。 CE₁ᵢがCE₂ᵢよりも優れている i が少なくとも 1 つ存在します。それ以外の場合、どちらの要素変換セットも他の要素の変換よりも優れておらず、互いほど良くはありません。
ELᵢがスプレッド要素でない場合は、式からのより適切な変換を使用して変換比較が行われます。ELᵢがスプレッド要素の場合は、スプレッド コレクションの要素型からE₁またはE₂へのにより優れた変換を使用します。
次に当てはまる場合、C₁ は より優れたC₂:
と はともに型の ではなく、 は暗黙的に に変換可能であり、 は暗黙的に に変換できません。 -
E₁にはE₂への ID 変換がありません。また、E₁への要素変換はE₂への要素変換よりも優れています。 -
E₁にはE₂への ID 変換があり、次のいずれかが保持されます。T₁はSystem.ReadOnlySpan<E₁>、T₂はSystem.Span<E₂>、または-
T₁はSystem.ReadOnlySpan<E₁>またはSystem.Span<E₁>であり、T₂は要素型E₂を持つ array_or_array_interface
それ以外の場合は、どちらのコレクション型も優れていないため、結果はあいまいです。
メモ
これらのルールは、異なる要素型を受け取り、コレクション型間の変換を行わないオーバーロードを公開するメソッドは、空のコレクション式ではあいまいであることを意味します。 例
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