支持者问题: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];
- 转换为
new CollectionType(...)调用时,这些...arguments...函数用于确定相应的构造函数,并相应地传递。 - 在转换为
CollectionFactory.Create调用时,这些参数在元素参数之前ReadOnlySpan<ElementType>传递,所有这些...arguments...参数都用于确定适当的Create重载,并相应地传递。 - 当转换为接口(如
IDictionary<,>)时,只允许使用单个参数。 它实现一个已知的 BCL 比较器接口,并用于控制最终实例的键比较语义。
已选择此语法作为以下语法:
- 将所有信息保留在语法中
[...]。 确保代码仍然清楚地指示正在创建的集合。 - 不表示调用
new构造函数(当不是创建所有集合的方式时)。 - 并不意味着多次创建/复制集合的值(如后缀
with { ... }可能)。 - 不违反作顺序,尤其是 C# 的一致的从左到右表达式计算排序语义。 例如,在计算用于填充集合的表达式 后 ,它不会计算用于构造集合的参数。
- 不强制用户读取到(可能较大的)集合表达式的末尾,以确定核心行为语义。 例如,必须查看百行字典的末尾,才发现,是的,它使用的是正确的键比较器。
- 既不微妙,也不太详细。 例如,使用
;而不是,指示参数是一个很容易错过的语法片段。with()仅添加 6 个字符,并且很容易脱颖而出,尤其是关键字的with语法着色。 - 读得不错。 “这是一个集合表达式”with“这些参数,由这些元素组成。
- 解决了字典和集的比较器的需求。
- 确保任何用户需要传递参数,或者我们自己在将来的比较器以外的任何需求已经处理。
- 与任何现有代码(用于 https://grep.app/ 搜索)不冲突。
设计理念
以下部分介绍先前的设计理念讨论。 包括为什么拒绝某些形式。
我们提供了两个主要方向来提供此用户定义的数据。 第一个是 仅 对 比较器 空间中的值(我们定义为从 BCL IComparer<T> 或 IEqualityComparer<T> 类型继承的类型)中的特殊情况值。 第二种是提供通用机制,用于在创建集合表达式时向最终调用的 API 提供任意参数。 主要 字典表达式 规范显示我们如何执行前者,而此规范则寻求执行后者。
如果我们想将其扩展到任意论点,对刚刚通过比较器的解决方案的检查揭示了他们方法中的弱点。 例如:
重用元素语法,就像我们采用以下形式一样:
[StringComparer.OrdinalIgnoreCase, "mads": 21]这在一个空间中非常有效,其中KeyValuePair<,>比较器不会继承自常见类型。 但它在一个可能做到的世界里崩溃了:HashSet<object> h = [StringComparer.OrdinalIgnoreCase, "v"]这是传递比较器吗? 或者尝试将两个对象值放入集中?将参数与语法微妙的元素(例如使用分号而不是逗号分隔它们
[comparer; v1])分隔出来。 如果用户意外写入[1; 2](并获取传递“1”的集合(例如,“capacity”参数List<>,并且仅包含单个值“2”),则存在非常令人困惑的情况,即当用户打算[1, 2](包含两个元素的集合)。
因此,为了支持任意参数,我们认为需要一个更明显的语法来更清楚地划定这些值。 其他几个设计问题也在此空间中提出了。 无特定顺序,包括:
解决方案不明确,导致代码中断,人们今天可能与集合表达式一起使用。 例如:
List<Widget> c = [new(...), w1, w2, w3];这是合法的,表达式
new(...)是创建一个新小组件的“隐式对象创建”。 我们不能重新调整其用途,以将参数List<>传递给 's 构造函数,因为它 肯定会 破坏现有代码。语法不会扩展到构造外部
[...]。 例如: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_element序列with(。
这有两种方式是有益的。 首先,编译器实现只需要查看它看到的紧接着的令牌,以确定要分析的元素类型。 其次,相应地,用户可以轻松了解他们所拥有的某种元素,而无需在心理上尝试分析后续内容,看看他们是否应该将其视为 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];
这些形式似乎“阅读”相当好。 在所有这些情况下,代码都是“创建集合表达式,使用以下参数”with“传递以控制最终实例,然后是用于填充它的后续元素。 例如,第一行“创建字符串列表”with“,容量为要分散到其中的值的两倍”
重要的是,此代码几乎没有被忽视的机会,如: [arg; element],同时添加最少的详细程度,并具有大量的灵活性来传递任何所需的参数。
从技术上讲,这将是一项重大更改,就像调用预先存在的方法with一样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 比较器 类型继承的单个参数。
转换
集合表达式的 转换 部分按以下方式更新:
> 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_listwith_element不会影响转换是否存在。 只是本身的存在或缺失 with_element 。 此处的直觉只是,如果集合表达式没有写入集合表达式(如 [x, y, z]),则必须能够调用没有参数的构造函数。 如果具有 [with(...), x, y, z] 该函数,则可以调用相应的构造函数。 这也意味着 不能 使用无参数构造函数调用的类型 可以 与集合表达式一起使用,但 前提是 该集合表达式包含 a with_element.
下面给出了一个with_element影响建筑的实际决定。
建筑
构造按如下所示进行更新。
集合表达式的元素按从左到右的顺序计算。 在 集合参数中,按从左到右的顺序计算参数。 每个元素或参数的计算方式完全相同一次,任何进一步引用都引用此初始计算的结果。
如果包含 collection_arguments 并且不是集合表达式中的第一个元素,则报告编译时错误。
如果 参数列表 包含任何具有 动态 类型的值,则报告编译时错误(LDM-2025-01-22)。
Constructors
如果目标类型是实现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]属性的集合表达式,创建方法如下,从集合表达式更新:创建方法。
特性
[CollectionBuilder(...)]指定要调用的方法的生成器类型和方法名称,以构造集合类型的实例。生成器类型必须是非泛型的
class或struct。首先,确定适用于
的 创建方法集合。 它由符合以下要求的方法组成:
- 该方法必须具有
[CollectionBuilder(...)]属性中指定的名称。- 必须在生成器类型上直接定义该方法。
- 该方法必须是
static。- 使用集合表达式的地方必须能访问该方法。
- 方法的 arity 必须与集合类型的 arity 匹配。
- 该方法必须具有类型
System.ReadOnlySpan<E>的最后一个参数,由值传递。- 从方法返回类型到集合类型,存在标识转换、隐式引用转换或装箱转换。
在基类型或接口上声明的方法将被忽略,它不属于
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>(IEqualityComparer<K>)Dictionary<K, V>()Dictionary<K, V>(int)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 安全性
我们调整 collection-expressions.md#ref-safety 规则以考虑元素 with() 。
另请参阅 §16.4.15 安全上下文约束。
创建方法
本部分适用于目标类型满足 CollectionBuilderAttribute 方法中定义的约束的集合表达式。
安全上下文通过修改 collection-expressions.md#ref-safety 中的子句来确定(粗体更改):
- 如果目标类型是具有 create 方法的 ref 结构类型,则集合表达式的安全上下文是调用 create 方法的安全上下文,其中参数是
with()元素参数,后跟集合表达式作为最后一个参数(ReadOnlySpan<E>参数)的参数。
方法参数必须与约束匹配应用于集合表达式。 与上述 安全上下文 确定类似, 方法参数必须与约束匹配 ,方法是将集合表达式视为 create 方法的调用,其中参数是 with() 元素参数,后跟集合表达式作为最后一个参数的参数。
构造函数调用
本部分适用于目标类型满足 构造函数中定义的约束的集合表达式。
对于以下格式的 ref 结构类型的 集合表达式:
[with(a₁, a₂, ..., aₙ), e₁, e₂, ..., eₙ]
集合表达式的安全 上下文 是以下表达式中最窄的安全 上下文 :
- 对象创建表达式
new C(a₁, a₂, ..., aₙ),其中目标C类型 - 元素表达式
e₁, e₂, ..., eₙ(表达式本身或分布元素的分布值)。
方法参数必须与约束匹配应用于集合表达式。 通过将集合表达式视为每个低级别结构改进.md#rules-for-object-initializers 创建表单new C(a₁, a₂, ..., aₙ) { e₁, e₂, ..., eₙ }的对象,来应用约束。
- 表达式元素被视为集合元素初始值设定项。
- 通过暂时假设
C具有方法Add(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
需要参数的目标类型
是否应支持集合表达式转换到必须提供参数的目标类型,因为所有构造函数或工厂方法都需要至少一个参数?
此类类型可以与包含显式 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
__arglist应该在元素中with()受支持?
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>(请参阅 “构造 ”,了解每个接口的潜在签名)。
从已知类型使用可访问的构造函数具有以下含义:
- 参数名称(可选)
params直接取自参数。 - 包括所有可访问的构造函数,即使这可能对集合表达式没有用,例如
List(IEnumerable<T>)允许IList<int> list = [with(1, 2, 3)];的构造函数。 - 构造函数集可能取决于 BCL 版本。
重新推荐:使用已知类型的可访问构造函数。 我们保证我们将使用这些类型,因此,这只是“掉出”,是构造这些值的最清晰和最简单的路径。
对于 非可变 接口类型,选项类似:
- 不执行任何操作。 这
- 使用独立于特定类型的签名,尽管唯一的方案可能
new(IEqualityComparer<K> comparer)适用于IReadOnlyDictionary<K, V>C#14。
使用来自某些已知类型的可访问构造函数(可变接口类型策略)是不可行的,因为与任何特定的现有类型没有关系,并且可以使用和/或合成的最终类型。 因此,编译器必须有奇怪的新要求,编译器能够将任何现有构造函数(即使它发展)映射到它实际生成的非可变实例。
重新推荐:使用独立于特定类型的签名。 而且,对于 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(...)] 在此功能之前,它表示具有一个元素的集合表达式,这是调用 with-invocation-expression 的结果。 在此功能之后,它是一个集合,其中包含传递给它的自变量。
只有当用户选取特定语言版本(如 C#-14/15?)时,我们才希望发生此中断。 换句话说,如果它们位于较旧的langversion 上,则会获取以前的分析逻辑,但在较新版本上,它们将获得较新的分析逻辑。 在还是 我们始终 希望它具有较新的分析逻辑,即使在较旧的 langversion 上?
我们对这两种策略都有先前的艺术。
required例如,无论 langversion 如何,始终使用新逻辑进行分析。
record/field而其他人则根据语言版本进行分析逻辑。
最后,这与 KVP 元素的key:value语法重叠和影响Dictionary Expressions。 我们希望为任何 lang 版本以及[with(...)]它本身以及诸如或[expr : with(...)]之类的[with(...) : expr]内容建立所需的行为。