允许一种新形式的字符串文字,以最少三个"开头"" 字符(但没有最大值),可选地后跟新的_行,即字符串的内容,然后以与文本开头相同数量的引号结尾。例如:
因为嵌套内容本身可能希望使用""" 然后开始/结束分隔符可以更长,如下所示:
为了使文本易于阅读,并允许开发人员喜欢在代码中使用缩进,这些字符串文字在生成最终文字值时会自然删除最后一行中指定的缩进。例如,形式的文字:
这使得代码看起来很自然,同时仍能生成所需的文本,如果需要使用专门的字符串操作例程,还可以避免运行时成本。
如果不需要缩进行为,那么也可以这样禁用:
还支持单行表单。它从最少三个开始""" 字符(但没有最大值),字符串的内容(不能包含任何新的_行字符),然后以与文本开头相同数量的引号结尾。例如:
还支持插值原始字符串。在本例中,字符串指定开始插值所需的大括号数(由文本开头的美元符号数决定)。任何大括号少于该大括号的大括号序列都被视为内容。例如:
C#缺乏一种通用的方法来创建简单的字符串文字,这些文字可以有效地包含任意文本。现在所有C#字符串文字形式都需要某种形式的转义,以防内容使用某些特殊字符(如果使用分隔符,则始终如此)。这可以防止文本中包含其他语言(例如,XML、HTML或JSON文本)。
目前所有在C#中形成这些文字的方法都会迫使用户手动转义内容。在这一点上编辑可能会非常烦人,因为转义是不可避免的,必须在内容中出现时处理。这对正则表达式来说尤其痛苦,尤其是当它们包含引号或反斜杠时。即使是34岁" 字符串、引号本身必须转义,导致C#和正则表达式的混合。{和}在$"中同样令人沮丧" 串。
问题的关键是我们所有的字符串都有一个固定的开始/结束分隔符。在这种情况下,我们总是需要一个转义机制,因为字符串内容可能需要在其内容中指定结束分隔符。这是特别有问题的,因为分隔符#34;在许多语言中非常普遍。
为了解决这个问题,这个建议允许使用灵活的开始和结束分隔符,以便它们总是以一种不会与字符串内容冲突的方式生成。
提供一种机制,允许用户提供所有字符串值,而不需要任何转义序列。因为所有字符串都必须是无转义序列的可表示字符串,所以用户必须始终能够指定分隔符,以确保不会与任何文本内容冲突。
以相同的方式支持插值。如上所述,由于所有字符串都必须是不带转义符的可表示字符串,因此用户必须始终能够指定一个插值分隔符,以确保不会与任何文本内容发生冲突。重要的是,使用插值分隔符({和})的语言应该是一流的,而且使用起来不会很痛苦。
多行字符串文字在代码中应该看起来令人愉快,并且不应该使编译单元中的缩进看起来奇怪。重要的是,本身没有缩进的文本值不应该被强制占据文件的第一列,因为这可能会中断代码流,并且看起来与围绕它的其余代码不一致。这种行为应该易于重写,同时保持文字清晰易读。
对于本身不包含新行或以引号(";)开头或结尾的所有字符串字符,应该可以在一行中表示字符串文字本身。此外,我们还可以通过额外的复杂度对其进行细化,以说明:对于所有本身不包含新行(但可以以引号开头或结尾";字符)的字符串,应该可以在一行上表示字符串文字本身。有关更多详细信息,请参阅“缺点”部分中的扩展提案。
string_literal:regular_string_literal | verbatim_string_literal | raw_string_literal;原始字符串文字:单行原始字符串文字;多行原始字符串文字;原始字符串文字分隔符:""" | """" | """"" | 等原始内容:非新行+;单行原始字符串文字:原始字符串文字分隔符原始内容原始字符串文字分隔符;多行_原始_字符串_文字:原始_字符串_文字_分隔符空格*新行(原始_内容|新行)*新行空格*原始_字符串_文字_分隔符;非新行:<;任何非新的unicode字符;
原始字符串文字的结束分隔符必须与起始分隔符匹配。如果起始分隔符是""""" 结尾分隔符也必须是该分隔符。
然后,它将继续使用与起始引号位于同一行的内容。同一行中的这些内容可以是空白的,也可以是非空白的'空白和#39;是'的同义词;完全是空白;。
如果同一行的内容为非空,则不能再添加其他内容。换句话说,要求文本在同一行上以相同数量的引号结尾。
如果同一行上的内容为空,则文字可以继续使用一个新的_行以及随后的多个内容行和新的_行。然后,它以一个新的_行结束,其中包含一定数量(可能为零)的空格和与文本开头相同数量的引号。
起始和结束的原始字符串文字分隔符之间的部分用于按以下方式形成原始字符串文字的值:
在单_行_原始_字符串_文本的情况下,文本的值正好是起始和结束原始_字符串_文本_分隔符之间的内容。
在多行原始字符串文本的情况下,初始空白*新行和最终新行空白*不是字符串值的一部分。然而,原始的_string _literal _分隔符终端前面的最后一个空格*部分被认为是';缩进空格';也会影响其他台词的解读。
要获得最终值,需要遍历(原始内容|新行)*序列,并执行以下操作:如果是新行,则新行的内容将添加到最终字符串值。
如果不是a';空白和#39;原始内容(即not_new_line+包含非空白字符):39;缩进空格';必须是原始内容的前缀。否则就是一个错误。
第';缩进空格';从原始内容的开头剥离,剩余部分添加到最终字符串值。
如果是a';空白和#39;原始内容(即非新行+完全是空白):39;缩进空格';必须是原始内容的前缀,或者原始内容必须是'的前缀;缩进空格';。否则就是一个错误。
大部分';缩进空格';从原始内容的开头剥离,剩余部分添加到最终字符串值。
单个_line_raw_string_literal无法表示包含新行值的字符串。单个_行_原始_字符串_文字不参与';缩进空格';修剪。它的值始终是起始分隔符和结束分隔符之间的精确字符。
由于多行\u原始\u字符串\u文本忽略最后一个内容行的最后一个新\u行,因此下面表示一个没有起始新行和终止新行的字符串
这与忽略起始新线的方式保持了对称性,并提供了一种统一的方式来确保';缩进空格';可以随时调整。要用终端新行表示字符串,必须提供额外的一行,如下所示:
单个_行_原始_字符串_文字不能表示以引号(";)开头或结尾的字符串值尽管缺陷部分提供了对该提案的补充,说明了如何支持该提案。
多行原始字符串文字以初始原始字符串文字分隔符后面的空格*新行开头。分隔符后面的内容将被完全忽略,并且在确定字符串的值时不会以任何方式使用。这允许一种机制来指定一个原始字符串,其内容以"开头;角色本身。例如:
原始字符串文字也可以表示以引号(";)结尾的内容。这是受支持的,因为终止分隔符必须位于其自己的行上。例如:
var v1=""" "该字符串的内容以引号#34开头和结尾"""
var v1=""" ""该字符串的内容以两个引号开头和结尾"" """
a';空白和#39;原始内容可以是'的前缀;缩进空格';或者';缩进空格';必须是它的前缀,这有助于确保不会出现混合空格的混乱场景,尤其是因为不清楚该行应该发生什么。例如,以下情况是非法的:
这里是';缩进空格';是九个空格字符,但';空白和#39;原始内容不以该前缀开头。至于这是怎么回事,目前还没有明确的答案<;选项卡>;这条线应该被处理。应该忽略它吗?应该和……一样吗&书信电报;tab>;?因此,为了避免混淆,将其定为非法似乎是最明确的。
在这两种情况下,';缩进空格';将有九个空间。在这两种情况下,我们将尽可能多地删除前缀,以';空白和#39;每种情况下的原始内容都是空的(不包括每一行新内容)。这使得用户在复制/粘贴或编辑这些行时,不必看到这些行上的空白,也不必担心这些行上的空白。
第';缩进空格';还是九个空间。不过,在这里,我们将删除尽可能多的';缩进空格';以及';空白和#39;原始内容将为最终内容提供一个单独的空间。这允许在这些行中的内容确实需要保留空白的情况下使用。
这是因为原始字符串的开头必须有一个新的_行(它有),但结尾也必须有一个新的_行(它没有)。最小合法原始字符串文字为:
普通插值字符串中的插值(例如$";…";)现在通过使用{字符开始插值和使用{转义序列插入实际的大括号字符来支持。使用相同的机制将违反本提案的目标';1';和';2'(例如JavaScript、JSON、正则表达式,甚至嵌入式C#)现在需要转义,从而取消了原始字符串文本的用途。
为了支持插值,我们以不同于普通$34的方式引入它们;插入字符串。具体来说,插值的_raw_string_literal将以一些$字符开始。这些字符的计数指示在文本内容中需要多少{(和})字符来分隔插值。重要的是,卷发牙套仍然没有逃脱机制。相反,就像引用(";)文本本身始终可以确保为插值指定分隔符,这些分隔符肯定不会与字符串的任何其他内容冲突。例如,包含插值孔的JSON文本可以这样写:
这里是{{…}匹配$$分隔符前缀指定的两个大括号的必要计数。在单个$的情况下,这意味着插值被指定为{…}与普通插值字符串文字一样。重要的是,这意味着带有N$个字符的插值文字可以有一个2*N-1大括号的序列(一行中的同一类型)。最后N个大括号将开始(或结束)插值,剩下的N-1个大括号将只是内容。例如:
在这种情况下,内部的两个{和}}大括号属于插值,外部的奇异大括号只是内容。因此,上面的字符串相当于内容X{2}Z。使用2*N(或更多)大括号总是一个错误。要将较长的大括号序列作为内容,必须相应增加$字符的数量。
内插原始字符串文字:单行内插原始字符串文字;多行内插原始字符串文字;插入的_原始_字符串_开始:$|$$|$$$|等;内插原始字符串文字分隔符:内插原始字符串起始原始字符串文字分隔符;单行插入的原始字符串文字:插入的原始字符串文字分隔符插入的原始内容原始字符串文字分隔符;multi_line_interpolated_raw_string_literal:interpolated_raw_string_literal_分隔符空格*新行(interpolated_raw_content |新行)*新行空格*raw_string_literal_分隔符;插值的原始内容:(非新的线|原始插值)+;原始插值:原始插值开始插值原始插值结束;原始插值开始:{{{{{{{}等;原始插值结束:}}}}}}}等;
以上内容与原始字符串文字的定义类似,但有一些重要区别。插入的_原始_字符串_文字应解释为:
它从至少一个美元符号(但没有上限)开始,然后是三个引号(也没有上限)。
然后,它将继续使用与起始引号位于同一行的内容。同一行的内容可以是空白,也可以是非空白'空白和#39;是'的同义词;完全是空白;。
如果同一行的内容为非空,则不能再添加其他内容。换句话说,要求文本在同一行上以相同数量的引号结尾。
如果同一行上的内容为空,则文字可以继续使用一个新的_行以及随后的多个内容行和新的_行。内容行可以在任何位置包含多个原始插值事件。原始_插值必须以相等数量的大括号({)开始,作为文字开头的美元符号数。
原始插值将遵循§11.7.3规定的正常规则。任何原始的_插值必须以与美元符号和开放大括号相同数量的闭大括号(})结束。
任何插值本身都可以包含新行,其方式与普通逐字字符串文字(@";";)中的插值相同。
然后,它以一个新的_行结束,其中包含一定数量(可能为零)的空格和与文本开头相同数量的引号。
插值字符串值的计算遵循与普通原始字符串文字相同的规则,但已更新以处理包含原始字符串插值的行。构建字符串值的方式也是一样的,只是将插值孔替换为这些表达式在运行时生成的任何值。如果插值的_raw_string_literal被转换为FormattableString,则插值的值将按各自的顺序传递给参数数组,以生成FormattableString。创造插入的_raw_string_literal在'之后的其余内容;缩进空格';将用于生成传递给FormattableString的格式字符串。创建,除非在发生原始_插值的每个位置有适当编号的{N}内容(如果其插值形式为表达式';,';常量_表达式,则为{N,常量})。
上述规范存在歧义。特别是当{文本中的{部分和{插值的{部分邻接时。例如:
这可以解释为:{{{order_number}}或{{{{order_number}}}。然而,由于前者是非法的(没有任何C#表达式可以以{开头),这样解释是没有意义的。所以我们用后一种方式解释,最里面的{和}大括号构成插值,最外面的大括号构成文本。将来,如果语言支持任何用大括号括起来的表达式,这可能会成为一个问题。然而,在这种情况下,建议写这样一个案例:{({some_new_expression_form}}}。在这里,括号将有助于从文字/插值的其余部分指定表达式部分。这已经优先于三元条件表达式需要如何包装,以避免与插值的格式/对齐说明符冲突(例如{(x?y:z)})。
原始字符串文字增加了语言的复杂性。我们已经有了许多字符串文字形式,用于多种用途"" 字符串,@"" 字符串和$"" 弦已经有了很大的力量和灵活性。但它们都缺乏一种方式来提供不需要逃逸的原始内容。
... 此外,我们还可以通过额外的复杂度对其进行细化,以说明:对于所有本身不包含新行(但可以以引号开头或结尾";字符)的字符串,应该可以在一行上表示字符串文字本身。
那';因为我们没有办法知道开头或结尾的报价(";)应该属于内容,而不是分隔符本身。如果这是我们想要支持的一个重要场景,我们可以添加一个并行的''' 按照"进行施工"" 类型使用这种平行结构,一个以"开头和结尾的单行字符串;可以很容易地写成'''"该字符串以引号"开头和结尾''' 以及平行结构#34""'这个字符串以撇号开头和结尾'""". 这可能也有助于支持从视觉上分离引号字符,这可能有助于嵌入主要使用一个引号字符而不是另一个引号字符的语言。
https://github.com/dotnet/csharplang/discussions/89这里介绍了许多选项。替代品有很多,但我觉得太过复杂和糟糕的人体工程学。这种方法为简单起见,只需不断增加起始/结束引号长度,直到不担心与字符串内容冲突为止。它还允许您编写的代码看起来缩进良好,同时仍然生成大多数代码想要的dededented文本。
不过,最有趣的潜在变化之一是对这些原始字符串文本使用`(或```)围栏。这将有几个好处:
这对markdown来说很熟悉。尽管这本身可能不是一件好事,因为用户可能会期望降价解释。
在大多数情况下,原始字符串文字只需以单个字符开头和结尾,而在更罕见的包含回号的内容中,则只需要多个字符。
将来用`` xml扩展它会感觉很自然,这又类似于降价。当然,这也适用于""" 类型
但总的来说,这里的净收益似乎很小。在里面
......