次の方法で共有


コレクション式の引数

チャンピオン号: 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];
  1. new CollectionType(...)呼び出しに変換する場合、これらの...arguments...は適切なコンストラクターを決定するために使用され、それに応じて渡されます。
  2. CollectionFactory.Create呼び出しに変換する場合、これらの...arguments...は前に ReadOnlySpan<ElementType> 要素引数と共に渡され、そのすべてが適切なCreateオーバーロードを決定するために使用され、それに応じて渡されます。
  3. インターフェイス ( IDictionary<,> など) に変換する場合は、1 つの引数のみが許可されます。 これは、既知の BCL 比較子インターフェイスの 1 つを実装し、最終的なインスタンスのキー比較セマンティクスを制御するために使用されます。

この構文は、次のように選択されました。

  1. すべての情報を [...] 構文内に保持します。 コードが作成中のコレクションを明確に示していることを確認します。
  2. new コンストラクターを呼び出すわけではありません (すべてのコレクションが作成される方法ではない場合)。
  3. コレクションの値を複数回作成またはコピーすることを意味しません (後置 with { ... } など)。
  4. 特に C# の一貫した左から右の式の評価順序セマンティクスでは、操作の順序を競合しません。 たとえば、コレクションの設定に使用される式を評価 した後 、コレクションの構築に使用される引数は評価されません。
  5. コア動作セマンティクスを決定するために、ユーザーに (大規模な可能性がある) コレクション式の最後まで読み取りを強制しません。 たとえば、100 行の辞書の末尾まで表示する必要がある場合は、正しいキー比較子を使用していたことがわかります。
  6. どちらも微妙ではありませんが、過度に冗長ではありません。 たとえば、引数を示すために,の代わりに;を使用することは、非常に簡単な構文です。 with() 6 文字しか追加されておらず、特に with キーワードの構文の色分けでは、簡単に目立ちます。
  7. 読み取りがうまく行く。 "これは、これらの要素で構成される、これらの引数を持つコレクション式です。"
  8. 辞書とセットの両方の比較子の必要性を解決します。
  9. ユーザーが引数を渡す必要がある場合、または将来の比較子を超えるニーズが既に処理されていることを確認します。
  10. 既存のコードと競合しません ( https://grep.app/ を使用して検索します)。

設計理念

以下のセクションでは、以前の設計哲学の議論について説明します。 特定のフォームが拒否された理由を含む。

このユーザー定義データを提供するには、主に 2 つの方向があります。 1 つ目は、比較子空間内の特殊なケースのみの値です (BCL のIComparer<T>またはIEqualityComparer<T>型から継承する型として定義します)。 2 つ目は、コレクション式を作成するときに、最終的に呼び出された API に任意の引数を指定する一般化されたメカニズムを提供することです。 1 次 辞書式 の仕様は、前者を行う方法を示していますが、この仕様では後者を実行します。

単に合格する 比較子 の解決策を調べると、 任意の引数に拡張する場合のアプローチの弱点が明らかになります。 例えば次が挙げられます。

  1. [StringComparer.OrdinalIgnoreCase, "mads": 21]という形式で行うのと同様に、要素構文を再利用します。 これは、 KeyValuePair<,> と比較子が共通の型から継承されない空間で適切に機能します。 しかし、それはあるかもしれない世界で分解する: HashSet<object> h = [StringComparer.OrdinalIgnoreCase, "v"]。 これは比較子に沿って渡されていますか? または、2つのオブジェクト値をセットに入れようとしていますか?

  2. 引数と要素を微妙な構文で区切る (コンマの代わりにセミコロンを使用して [comparer; v1]で区切るなど)。 これは、ユーザーが意図した[1, 2] (2 つの要素を持つコレクション) を意図した場合に、誤って[1; 2]を書き込み (たとえば、List<>の 'capacity' 引数として '1' を渡し、単一の値 '2' のみを含むコレクションを取得する) 状況を非常に混乱させるリスクがあります。

このため、任意の引数をサポートするには、これらの値をより明確に区切るために、より明確な構文が必要であると考えています。 この空間では、他にもいくつかの設計上の懸念が生まれています。 順序は特にありませんが、次のようになります。

  1. このソリューションはあいまいではなく、現在コレクション式で使用している可能性が高いコードで中断を引き起こします。 例えば次が挙げられます。

    List<Widget> c = [new(...), w1, w2, w3];
    

    これは現在有効であり、 new(...) 式は新しいウィジェットを作成する "暗黙的なオブジェクトの作成" です。 これは、既存のコードを確実に中断するため、引数をList<>のコンストラクターに渡すように再利用することはできません。

  2. 構文が [...] コンストラクトの外部に拡張されないこと。 例えば次が挙げられます。

    HashSet<string> s = [...] with ...;
    

    これらの構文は、コレクションが最初に作成され、次に異なる形式に再作成され、データの複数の変換を意味し、望ましくない可能性のある高いコスト (生成されたものではない場合でも) を意味すると解釈できます。

  3. この空間でまったく使用する潜在的なキーワードとしてnew、混乱を招く可能性があります。 [...] already新しいオブジェクトが作成されることを示し、コレクション式の変換はコンストラクター以外の API (Create メソッド パターンなど) を通過する可能性があるためです。

  4. ソリューションが過度に冗長でないことを示します。 コレクション式の主要な価値提案は 簡潔さです。 そのため、フォームが大量の構文スキャフォールディングを追加すると、一歩後ろ向きに感じられ、コレクションを作成するために既存の 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として扱われます。

これは 2 つの方法で役立ちます。 最初に、コンパイラの実装では、解析する要素の種類を決定するために、見た直後のトークンのみを確認する必要があります。 次に、ユーザーは、次の要素を精神的に解析して、 with_element または expression_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];

これらの形式は、合理的に「読む」ように見えます。 いずれの場合も、コードは"コレクション式を作成し、最後のインスタンスを制御するために渡す次の引数と、それを設定するために使用される後続の要素を作成します。 たとえば、最初の行は"文字列のリストを作成し、それに分散しようとしている値の数の2倍の容量を持つ"。

重要なのは、このコードは、 [arg; element]などの形式のように見過ごされる可能性はほとんどありませんが、最小限の詳細度を追加し、必要な引数を柔軟に渡すことができます。

これは 技術的には 破壊的変更です。 with(...)could は、 withと呼ばれる既存のメソッドの呼び出しでした。 ただし、暗黙的に型指定された値を作成するための既知の推奨される方法であるnew(...)とは異なり、with(...)はメソッド名としてはるかに可能性が低く、メソッドに対して .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 比較子 型を継承する 1 つの引数を、変換先ディクショナリ インターフェイス型の 1 つをインスタンス化してその動作を制御するときに指定することもできます。

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. 

with_elementargument_list内の実際の引数は、変換が存在するかどうかには影響しません。 with_element自体の有無だけです。 ここでの直感は、コレクション式が( [x, y, z]のように)1つなしで書かれた場合、引数なしでコンストラクタを呼び出すことができる必要があるということです。 [with(...), x, y, z]がある場合は、適切なコンストラクターを呼び出すことができます。 これは、引数なしのコンストラクターで呼び出すことができる型は、コレクション式と共に使用できますが、with_elementを含むコレクション式の場合にのみ使用できることを意味します。

with_elementが建設に与える影響の実際の決定を以下に示します

建設

構築は次のように更新されます。

コレクション式の要素は、左から右の順序で評価されます。 コレクション引数内では、引数は左から右の順序で評価されます。 各要素または引数は 1 回だけ評価され、それ以降の参照はこの初期評価の結果を参照します。

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: Create メソッド

ターゲット型 定義[CollectionBuilder] 属性があるコレクション式の場合、 create メソッド はコレクション式から 更新 され、 create メソッドになります

[CollectionBuilder(...)]属性は、コレクション型のインスタンスを構築するために呼び出されるメソッドのビルダーの型とメソッドを指定します。

ビルダーの型は、非ジェネリック classまたはstructである必要があります。

まず、適用可能な作成メソッドのセットCMが決定されます。 これは、次の要件を満たすメソッドで構成されます。

  • メソッドには、 [CollectionBuilder(...)] 属性で指定された名前が必要です。
  • メソッドはビルダー で直接定義する必要があります。
  • メソッドは staticする必要があります。
  • このメソッドには、コレクション式を使用する場所からアクセスできる必要があります。
  • メソッドの アリティ は、コレクション型の アリティ と一致する必要があります。
  • メソッドには、System.ReadOnlySpan<E>型の最後のパラメーターを値渡しする必要があります。
  • メソッドの戻り値の型からコレクション型への ID 変換暗黙的な参照変換、またはボックス化変換があります

基本型またはインターフェイスで宣言されたメソッドは無視され、 CM セットの一部ではありません。

宣言C<S0, S1, …>にビルダー C<T0, T1, …>が関連付けられているコレクション式の場合B.M<U0, U1, …>()ターゲット型のジェネリック型引数は、最も外側の包含型から最も内側の型まで、ビルダー メソッドに順番に適用されます。

以前のアルゴリズムとの主な違いは次のとおりです。

  • create メソッドには、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>()Dictionary<K, V>(int)Dictionary<K, V>(IEqualityComparer<K>)、およびDictionary<K, V>(int, IEqualityComparer<K>)構造体の署名です。 値を構築するときに ( 変更可能なインターフェイス変換を参照)、それぞれの 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

ref safety

with()要素を考慮して collection-expressions.md#ref-safety ルールを調整します。

§16.4.15 セーフ コンテキスト制約も参照してください。

メソッドを作成する

このセクションは、 CollectionBuilderAttribute メソッドで定義されている制約をターゲット型が満たすコレクション式に適用されます。

セーフ コンテキストは、collection-expressions.md#ref-safety (太字で変更) の句を変更することによって決定されます。

  • ターゲット型が create メソッドを持つ ref 構造体型の場合、コレクション式のセーフ コンテキストは、create メソッドの呼び出しのセーフ コンテキストです。ここで、引数は、最後のパラメーター (ReadOnlySpan<E> パラメーター) の引数としてコレクション式の後に続くwith()要素の引数です。

メソッド引数は、コレクション式に適用される制約と 一致する必要があります 。 上記の セーフ コンテキスト の決定と同様に、メソッド引数は、コレクション式を create メソッドの呼び出しとして扱うことで制約と 一致する必要があります 。ここで、引数は with() 要素引数の後にコレクション式が最後のパラメーターの引数として続きます。

コンストラクター呼び出し

このセクションは、コンストラクターで定義されている制約をターゲット型が満たすコレクション式に適用 されます

次の形式の ref 構造体型 のコレクション式の場合:
[with(a₁, a₂, ..., aₙ), e₁, e₂, ..., eₙ]

コレクション式の セーフ コンテキスト は、次の式の セーフ コンテキスト の中で最も狭いコンテキストです。

  • オブジェクト作成式 new C(a₁, a₂, ..., aₙ)( C はターゲット型です)
  • 要素式 e₁, e₂, ..., eₙ (式自体、または拡散要素の場合はスプレッド値のいずれか)。

メソッド引数は、コレクション式に適用される制約と 一致する必要があります 。 この制約は、コレクション式を、低レベルの struct-improvements.md#rules-for-object-initializers ごとにnew C(a₁, a₂, ..., aₙ) { e₁, e₂, ..., eₙ }フォームのオブジェクト作成として扱うことによって適用されます。

  • 式要素は、コレクション要素初期化子であるかのように扱われます。
  • 拡散要素は、一時的に CAdd(SpreadType spread) メソッドがあり、 SpreadType がスプレッド値の型であると仮定して同様に扱われます。

回答済みの質問

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

コレクション ビルダー メソッドのパラメーターの順序

コレクション ビルダー メソッドの場合、span パラメーターはコレクション引数のパラメーターの前または後にする必要がありますか?

最初に要素を使用すると、引数を省略可能として宣言できます。

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) { ... }
}

解像 度: 要素の span パラメーターは、最後のパラメーターである必要があります。 LDM-2025-03-12

以前の言語バージョンの引数

以前の言語バージョンでコンパイルするときに with() のエラーが報告されますか、それともスコープ内の別のシンボルにバインド with ですか?

解像 度: 以前の言語バージョンでコンパイルする場合、コレクション式内の with の破壊的変更はありません。 LDM-2025-03-17

引数が必要なターゲット型

すべてのコンストラクターまたはファクトリ メソッドに少なくとも 1 つの引数が必要であるため、引数を指定する必要があるターゲット型に対してコレクション式の変換をサポートする必要がありますか?

このような型は、明示的な 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

解像 度: free でない限り、コレクション引数の __arglist はサポートされません。 LDM-2025-03-05

インターフェイス型の引数

インターフェイスターゲットの型に対して引数をサポートする必要がありますか?

ICollection<int> c = [with(capacity: 4)];
IReadOnlyDictionary<string, int> d = [with(comparer: StringComparer.Ordinal), ..values];
その場合、引数をバインドするときに使用されるメソッド シグネチャはどれですか?

変更可能なインターフェイスの種類の場合、オプションは次のとおりです。

  1. インスタント化に必要な既知の型 ( List<T> または Dictionary<K, V>) からアクセス可能なコンストラクターを使用します。
  2. ICollection<T>IList<T>new()new(int capacity)を使用する場合など、特定の種類に依存しない署名を使用します (各インターフェイスの潜在的な署名の構築を参照)。

既知の型からアクセス可能なコンストラクターを使用すると、次のような影響があります。

  • パラメーター名 (省略可能)、 paramsはパラメーターから直接取得されます。
  • IList<int> list = [with(1, 2, 3)];を許可するList(IEnumerable<T>)など、コレクション式には役に立たない可能性がありますが、アクセス可能なすべてのコンストラクターが含まれます。
  • コンストラクターのセットは、BCL バージョンによって異なります。

再変換: 既知の型からアクセス可能なコンストラクターを使用します。 私たちはこれらの型を使用することを保証しているので、これは単に「フォールアウト」であり、これらの値を構築するための最も明確で最も簡単なパスです。

変更できないインターフェイスの種類の場合、オプションは次のようになります。

  1. 何もしません。 これ
  2. 特定の種類に依存しない署名を使用しますが、C#14 のIReadOnlyDictionary<K, V>には唯一のシナリオがnew(IEqualityComparer<K> comparer)場合があります。

既知の型 (変更可能なインターフェイス型の戦略) からアクセス可能なコンストラクターを使用することは、特定の既存の型と関係がなく、最終的な型を使用したり合成したりする可能性があるため、実行可能ではありません。 そのため、コンパイラは、(進化しても) その型の既存のコンストラクターを、実際に生成する変更できないインスタンスにマップできる、奇妙な新しい要件が必要になります。

再入力: 特定の型に依存しない署名を使用します。 また、C# 14 では、IReadOnlyDictionary<K, V>new(IEqualityComparer<K> comparer)のみがサポートされています。これは、ユーザーがこれを提供できるようにするための使いやすさ/セマンティクスにとって重要であると感じる唯一の変更不可能なインターフェイスです。 今後の C# リリースでは、提供される正当な理由に基づいて、このセットの拡張を検討できます。

解像 度:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-04-23.md

引数は、インターフェイス ターゲット型でサポートされています。 変更可能なインターフェイスと変更できないインターフェイスの両方で、一連の引数がキュレーションされます。

必要なリスト (LDM の承認が必要) は、インターフェイス ターゲットの種類です

空の引数リスト

一部またはすべてのターゲット型に対して空の引数リストを許可する必要がありますか?

空の with() は、 with()と同じになります。 空でないケースとの一貫性を提供する可能性がありますが、新しい機能は追加されません。

空の 'with()' の意味は、一部のターゲット型よりも明確な場合があります。 - **constructors** が使用されている型の場合は、引数なしで該当するコンストラクターを呼び出します。 - **'CollectionBuilderAttribute'** の型の場合は、要素のみを持つ該当するファクトリ メソッドを呼び出します。 - **インターフェイス型**の場合は、引数を指定せず、既知または実装で定義された型を構築します。 - ただし、コレクション引数がサポートされていない **arrays** と **spans** の場合、'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(...)]を使用した言語の破壊的変更です。 この機能の前は、1 つの要素を持つコレクション式を意味します。これは、 with呼び出し式を呼び出した結果です。 この機能の後は、引数が渡されたコレクションです。

この中断は、ユーザーが特定の言語バージョン ( C#-14/15 など) を選択した場合にのみ発生しますか。 つまり、古い言語の場合は、以前の解析ロジックが取得されますが、新しいバージョンでは新しい解析ロジックが取得されます。 それとも、古い言語であっても、 に新しい解析ロジックが必要ですか?

私たちは両方の戦略のための先行技術を持っています。 requiredたとえば、langversion に関係なく、常に新しいロジックで解析されます。 一方、 record/field や他のユーザーは、言語バージョンに応じて解析ロジックを調整します。

最後に、KVP 要素のkey:value構文を導入するDictionary Expressionsとの重複と影響があります。 任意の言語バージョン、および独自の [with(...)] や、 [with(...) : expr][expr : with(...)]などに必要な動作を確立したいと考えています。