C#中的多行字符串字面量

1410

有没有一种简单的方法在C#中创建多行字符串字面量?

这是我现在拥有的代码:

string query = "SELECT foo, bar"
+ " FROM table"
+ " WHERE id = 42";

我知道PHP有

<<<BLOCK

BLOCK;

C#有类似的东西吗?


2
你的示例中没有换行符,你需要它们吗? - weiqure
12
不。我只是出于可读性和代码整洁的原因需要多行。 - Chet
10
在这种情况下,verbatim字符串包含换行符。如果您愿意,您可以使用@"...".Replace(Environment.NewLine,"")。 - weiqure
14
如果 42 是来自用户输入的,为避免 SQL 注入攻击,建议将其绑定为参数。 - Jens Mühlenhoff
3
@weiqure: Environment.NewLine不一定反映字符串中的换行符,因为换行符是按照它们在源代码中出现的位置来读取的。因此,即使每行使用了与目标系统上Environment.NewLine不同的不同换行符,也可以编写代码!请注意,这并不影响代码的执行。 - mmmmmmmm
14个回答

2099

您可以在字符串前面使用@符号形成一个verbatim string literal

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

当您使用此方法时,您也不需要转义特殊字符,除了Jon Skeet的答案中显示的双引号。


129
如果你的字符串包含双引号("),你可以通过如下方式对其进行转义:" "(两个双引号字符)。 - Muad'Dib
13
有没有一种方法可以在不创建新行的情况下完成上述操作?我有一段非常长的文本,希望在IDE中将其换行,而不必使用加号("hi "+ "there")的方式。 - noelicus
5
@noelicus - 不是特别的,但你可以采用weiqure上面提到的方法解决:@"my string".Replace(Environment.NewLine, "") - John Rasch
2
如何将 @ 和新的 C# 运算符 $ 结合使用? - Afshar Mohebi
20
@afsharm 你可以使用 $@"文本" - Patrick McDonald
显示剩余5条评论

703

在C#中它被称为逐字字符串常量,只需要在字面量前面加上@符号即可。这不仅允许多行,而且还可以关闭转义。例如:

string query = @"SELECT foo, bar
FROM table
WHERE name = 'a\b'";

这个字符串中包含了换行符(使用源代码中的任何换行符),对于 SQL 来说,这不仅是无害的,而且可能会增强字符串的可读性。但在其他地方可能不需要这些换行符,此时您需要删除它们或者一开始就不使用多行文本字符串字面量。

唯一需要转义的部分是,如果你想要一个双引号,你必须添加一个额外的双引号符号:

string quote = @"Jon said, ""This will work,"" - and it did!";

8
这个答案是不正确的;它引入了 OP 不想要的换行符。 - TamaMcGlinn
6
@TamaMcGlinn:我会在回答中加入一些内容——当问题提出时并不清楚。 - Jon Skeet

243

顺便提一下,使用C# 6.0,您现在可以将插入字符串与逐字字符串文字结合使用:

string camlCondition = $@"
<Where>
    <Contains>
        <FieldRef Name='Resource'/>
        <Value Type='Text'>{(string)parameter}</Value>
    </Contains>
</Where>";

15
很酷,我直到现在都不知道这个。 如果有人感兴趣:$字符串被称为“插值字符串”,您可以在此处详细阅读它:https://msdn.microsoft.com/en-us/library/dn961160.aspx - Hauke P.
tx, fixed the wording - Riegardt Steyn
5
我假设一个字面上的花括号必须要加倍,例如 $@"{{example literal text { fooString }. }}"。这可能会让一些人感到困惑,因为 Angular、React 和 Vue.js 使用相反的约定。 - Patrick Szalapski
这是一个很棒的发现。不过,如果你的插值字符串值调用了方法 - 例如:{date.ToString("yyyy-mm-dd")},你可能需要考虑@dav_i的答案,因为这种插值方式更加简洁,而且不必去处理双引号。 - AndrewGentry

144

我发现使用字符串字面值的问题在于,它可能会使你的代码看起来有点“奇怪”,因为为了避免字符串本身中出现空格,它必须完全左对齐:

    var someString = @"The
quick
brown
fox...";

噫。

因此,我喜欢使用的解决方案是将其与您的其余代码保持良好对齐:

var someString = String.Join(
    Environment.NewLine,
    "The",
    "quick",
    "brown",
    "fox...");
当然,如果您只想在SQL语句中逻辑地拆分行,而实际上并不需要换行符,您可以始终将Environment.NewLine替换为" "

3
非常干净,谢谢。另外,String.Concat的工作方式类似,并且不需要分隔符。 - Seth
12
尽管不美观,第一版“无需编写代码即可运行”。显然,第二个选项在运行时需要额外的开销来连接不同的字符串。 - iCollect.it Ltd
谢谢您的回复,但在属性verbatim的情况下,无法解决这个问题。例如:[Tooltip(@"Very long string.....")],你将无法运行任何代码。我原计划使用verbatim,因为当我使用全屏编辑器时,单行字符串参数看起来很难看。但是verbatim会向字符串本身添加其他不可见字符,所以我找不到其他方法。 - 5argon
1
虽然这段代码可以编译通过,看起来更美观,但它未能通过代码验证,因为它容易受到 SQL 注入攻击。你不能在运行时构造任何查询字符串,必须是一个常量。 - John Henckel
我也喜欢这种方法,因为它使插值更加简洁。想象一下,如果你想做类似于 "The", "Quick", $"{fox.color}", "Fox" 的事情。 - AndrewGentry
第二段代码片段比问题中的代码更冗长。而且可能稍微慢一些,因为它调用了String.Join方法。我没有看到这种技术的任何好处。与问题本身展示的相比。 - undefined

107

还有一个需要注意的问题是在使用string.Format中出现字符串常量时,需要转义花括号 '{' 和 '}'。

// this would give a format exception
string.Format(@"<script> function test(x) 
      { return x * {0} } </script>", aMagicValue)
// this contrived example would work
string.Format(@"<script> function test(x) 
      {{ return x * {0} }} </script>", aMagicValue)

14
有没有使用 "@" 符号并不影响结果。无论是否使用 "@" 符号,为了使 "{" 可以被打印出来,都需要将 "{{" 加倍。这涉及到 String.Format 的问题,而非字符串本身的内容。 - greenoldman
12
对于想要将JavaScript代码放入字符串中的人来说,这是一个值得注意的陷阱,这种情况在正文字符串字面量中比普通字符串更常见。 - Ed Brannin
2
在新的C# 6.0中,您可以使用索引属性运算符与逐字字符串文字一起使用(例如$@"Value is {this.Value}";) - Riegardt Steyn
2
@Heliac 我想你的意思是插值字符串也可以使用该语法表示字面量。var query = $@" select foo, bar from table where id = {id} "; - brianary
我认为这个答案已经过时了。$@{ 解释为“插值字符串”。这意味着 @ 不再具有这样的功能。因此,括号不再需要转义。【我没有进行过测试。】 - undefined

87

为什么人们经常混淆字符串和字符串字面值?被采纳的答案是回答了不同的问题,而不是这个问题。

我知道这是一个老话题,但我可能和楼主有同样的疑问,看到人们一直误解它真的很让人沮丧。或者也许我自己理解错了,我不知道。

粗略地说,字符串是计算机内存中的一个区域,在程序执行期间包含一个可以映射到文本字符的字节序列。另一方面,字符串字面值是源代码的一部分,尚未编译,表示在程序执行期间用于初始化字符串的值。

在C#中,语句...

 string query = "SELECT foo, bar"
 + " FROM table"
 + " WHERE id = 42";

...不会生成三行字符串,而是一个单行字符串;由三个字符串连接而成(每个字符串都从不同的文字初始化),没有任何一个包含换行符修饰符。

在我的理解中,OP似乎询问的不是如何在编译后的字符串中引入类似源代码中的换行符,而是如何在源代码中清晰地分隔一个长的单行文本,而无需在编译后的字符串中引入断点。而且也不需要花费更长的执行时间来连接来自源代码的多个子字符串。就像JavaScript或C++中的多行字符串字面量内部的尾随反斜杠一样。

建议使用原始字符串,不要管 StringBuilderString.Join甚至是嵌套函数与字符串反转等等,让我觉得人们并没有真正理解这个问题。或者可能是我没有理解它。

据我所知,C#(至少在上个十年我还在使用的古老版本中)没有一个功能可以干净地生成可在编译期间解析而不是执行期间解析的多行字符串字面量

也许当前版本支持它,但我想分享一下我认为字符串和字符串字面量之间的差异。

更新:

(来自 MeowCat2012 的评论) 您可以。OP的“+”方法是最好的方法。根据规范,优化是有保证的:https://dev59.com/2nVC5IYBdhLWcg3wcwwm#288802


3
有些人(包括我自己)在看原问题时可能会感到困惑的原因是这里的文档格式(shell、php、perl等)包括换行符。因此,如果提问者正在将其与PHP的heredoc进行比较,那么包括换行符就不应该成为一个问题。 - Tanktalus
没错。据我所知,你的回答“不,你不能在C#中这样做”是正确的。 - TamaMcGlinn
1
我被告知在Java中,这样的连接字符串将在编译后的字节码中成为单个字符串文字。也许.Net也是这样做的吗? - Meow Cat 2012
6
可以。OP的“+”方法是最佳的选择。根据规范,优化是有保证的:https://dev59.com/2nVC5IYBdhLWcg3wcwwm#288802。尽管你是为数不多理解这个问题的人之一。是否可能删除或折叠潜在具有误导性但已被接受的答案? - Meow Cat 2012
2
谢谢你的提示,@MeowCat2012。看了一些反编译的代码,似乎确实是这样。 - Carvo Loco
就问题标题“字面上”而言,被接受的答案是对一个不同问题的很好的答案,而不是对这个问题的答案。根据问题中的代码,提问者并不“要求”结果创建一个“字面值”。他们只是在寻找一种更简单的语法来输入。提问者并没有说他们的代码示例是“错误”的,只是他们想要一个更简单的语法。另一方面,对于其他人来说,这个答案可能是一个有用的观察。 - undefined

67

添加多行:使用 @ 符号

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

在中间添加字符串值: 使用$

string text ="beer";
string query = $"SELECT foo {text} bar ";

多行字符串向中间添加值:使用 $@

string text ="Customer";
string query = $@"SELECT foo, bar
FROM {text}Table
WHERE id = 42";

FYI: {expression} 被称为插值表达式。以 $ 开头的整个字符串被称为插值字符串 - undefined

35
在C# 11 [2022]中,您将能够使用 原始字符串字面量。使用原始字符串字面量可以更轻松地使用“字符”而无需编写转义序列。
OP的解决方案:
string query1 = """
    SELECT foo, bar
    FROM table
    WHERE id = 42
    """;

string query2 = """
    SELECT foo, bar
    FROM table
    WHERE id = 42
    and name = 'zoo'
    and type = 'oversized "jumbo" grand'
    """;

有关原始字符串字面量的更多细节

有关完整详细信息,请参阅Raw String Literals GitHub问题; 以及博客文章C# 11预览更新 - 原始字符串文字,UTF-8等!


1
只是补充一下,你可以将此与插值相结合(这里)。 - derHugo
这是很好知道的。然而,这是对一个稍微不同的问题的回答。原因是,类似于@,换行符被包含在文本中。(这个回答和@之间的区别在于,"不需要转义。) - undefined

24

您可以使用@符号和""引号。

        string sourse = @"{
        ""items"":[
        {
            ""itemId"":0,
            ""name"":""item0""
        },
        {
            ""itemId"":1,
            ""name"":""item1""
        }
        ]
    }";

4
@符号用于保留字符串中的原始格式,而""则用于转义双引号。您应该解释清楚每个符号的含义。 - AFract
1
有没有一种快速的方法来处理这个问题,而不需要在粘贴的 JSON 中查找/替换所有双引号? - ryanwebjackson
1
请注意,当缩进行时,您还需要添加一堆空格。 - wenzzzel

15

我没看过这个,所以我会在这里贴出来(如果你想传递一个字符串,也可以这样做。)这个想法是你可以将字符串分成多行,并以任何你希望的方式添加自己的内容(同样也可以跨越多行)。在这里,“tableName”可以被传递到字符串中。

    private string createTableQuery = "";

    void createTable(string tableName)
    {

         createTableQuery = @"CREATE TABLE IF NOT EXISTS
                ["+ tableName  + @"] (
               [ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
               [Key] NVARCHAR(2048)  NULL, 
               [Value] VARCHAR(2048)  NULL
                                )";
    }

11
我认为相当危险:有人可以轻易地通过这种方式进行 SQL 注入攻击。 - Kai
7
只要您知道查询字符串中的所有变量(如果有的话!)都来自代码常量或其他安全的来源,例如 createTable("MyTable"); ,那么就没有问题。无论如何,OP的问题是关于如何直接在代码中输入多行字符串文字,而不是关于如何构造数据库查询语句本身的。 :) - dbeachy1
2
如果加号(+)的数量很多,最好使用字符串生成器(string builder)。 - galdin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接