我们有一个包含变量的单元测试,该变量包含一个很长的字符串。
问题是如何用代码编写此代码,而不会出现换行符问题或代码难以阅读。
在VB中有一个行继续符,在C#中有一个对等的字符吗?
Answers:
C#允许您将字符串分成多行,该术语称为verbatim literal:
string myString = @"this is a
test
to see how long my string
can be
and it can be quite long";
如果您正在寻找& _VB的替代方案,请使用+来加入您的行。
字符串常量
只需使用+运算符,然后将字符串分成易于理解的行即可。编译器会发现这些字符串是常量,并在编译时将它们连接起来。请参阅此处的《 MSDN C#编程指南》。
例如
const string myVeryLongString =
"This is the opening paragraph of my long string. " +
"Which is split over multiple lines to improve code readability, " +
"but is in fact, just one long string.";
IL_0003: ldstr "This is the opening paragraph of my long string. Which is split over multiple lines to improve code readability, but is in fact, just one long string."
字符串变量
请注意,在使用字符串插值法将值替换为字符串时,$字符需要在需要进行替换的每一行之前:
var interpolatedString =
"This line has no substitutions. " +
$" This line uses {count} widgets, and " +
$" {CountFoos()} foos were found.";
但是,这会对string.Format字符串的多次调用和最终串联(标记为***)造成负面的性能影响。
IL_002E: ldstr "This line has no substitutions. "
IL_0033: ldstr " This line uses {0} widgets, and "
IL_0038: ldloc.0 // count
IL_0039: box System.Int32
IL_003E: call System.String.Format ***
IL_0043: ldstr " {0} foos were found."
IL_0048: ldloc.1 // CountFoos
IL_0049: callvirt System.Func<System.Int32>.Invoke
IL_004E: box System.Int32
IL_0053: call System.String.Format ***
IL_0058: call System.String.Concat ***
尽管您可以$@用来提供单个字符串并避免性能问题,但是除非将空格放在{}其中(IMO看起来很奇怪),否则此问题与Neil Knight的回答相同,因为它将在行细目表中包括所有空格:
var interpolatedString = $@"When breaking up strings with `@` it introduces
<- [newLine and whitespace here!] each time I break the string.
<- [More whitespace] {CountFoos()} foos were found.";
注入的空白很容易发现:
IL_002E: ldstr "When breaking up strings with `@` it introduces
<- [newLine and whitespace here!] each time I break the string.
<- [More whitespace] {0} foos were found."
另一种方法是还原为string.Format。在这里,按照我的最初答案,格式化字符串是一个常量:
const string longFormatString =
"This is the opening paragraph of my long string with {0} chars. " +
"Which is split over multiple lines to improve code readability, " +
"but is in fact, just one long string with {1} widgets.";
然后这样评估:
string.Format(longFormatString, longFormatString.Length, CountWidgets());
但是,考虑到格式化字符串和替换标记之间的潜在分隔,要保持这种状态仍然很棘手。
FormattableString对象作为参数。这意味着它希望在已被插值并将其转换为普通字符串之前接收一个格式表字符串,但是串联操作不会保留FormattableString对象,而是会强制立即对字符串进行插值/格式化。
@"string here
that is long you mean"
但要小心,因为
@"string here
and space before this text
means the space is also a part of the string"
它还会转义字符串中的内容
@"c:\\folder" // c:\\folder
@"c:\folder" // c:\folder
"c:\\folder" // c:\folder
有关
在他的出色回答中,StuartLC引用了一个相关问题的答案,并提到在{expression}插值字符串的内部放置换行符“看起来很奇怪”。大多数人都同意,但是通过使用{expression}解析为default(String),即null(特别是不是 String.Empty)的专用块,可以在某种程度上缓解不愉快的源代码效应,并且不会造成任何运行时后果。
(尽管是次要的)重点是不要弄乱或污染您的实际表达式内容,而应为此目的使用专用令牌。因此,如果您声明一个常量,例如:
const String more = null;
...然后一行可能太长而无法在源代码中查看,例如...
var s1 = $"one: {99 + 1} two: {99 + 2} three: {99 + 3} four: {99 + 4} five: {99 + 5} six: {99 + 6}";
...可以这样写。
var s2 = $@"{more
}one: {99 + 1} {more
}two: {99 + 2} {more
}three: {99 + 3} {more
}four: {99 + 4} {more
}five: {99 + 5} {more
}six: {99 + 6}";
或者,您可能更喜欢对同一件事使用不同的“奇数”方法:
// elsewhere:
public const String Ξ = null; // Unicode '\u039E', Greek 'Capital Letter Xi'
// anywhere:
var s3 = $@"{
Ξ}one: {99 + 1} {
Ξ}two: {99 + 2} {
Ξ}three: {99 + 3} {
Ξ}four: {99 + 4} {
Ξ}five: {99 + 5} {
Ξ}six: {99 + 6}";
实际上,看起来我们也可以不用延续符号来做到这一点:
var s4 = $@"one: {99 + 1
}two: {99 + 2
}three: {99 + 3
}four: {99 + 4
}five: {99 + 5
}six: {99 + 6}";
这四个示例string在运行时都产生相同的结果,在这种情况下,它们全部在一行上:
one: 100 two: 101 three: 102 four: 103 five: 104 six: 105
正如Stuart所建议的,这两个示例都通过不使用IL来保留IL性能。 +串联字符串来。尽管在我的新示例中,较长的格式字符串确实存储在IL中,并因此存储在可执行文件中,但它所引用的空占位符并未初始化,并且在运行时没有多余的串联或函数调用。为了进行比较,以下是上述两个示例的IL。
第一个例子的IL
ldstr "one: {0} two: {1} three: {2} four: {3} five: {4} six: {5}"
ldc.i4.6
newarr object
dup
ldc.i4.0
ldc.i4.s 100
box int32
stelem.ref
dup
ldc.i4.1
ldc.i4.s 101
box int32
stelem.ref
dup
ldc.i4.2
ldc.i4.s 102
box int32
stelem.ref
dup
ldc.i4.3
ldc.i4.s 103
box int32
stelem.ref
dup
ldc.i4.4
ldc.i4.s 104
box int32
stelem.ref
dup
ldc.i4.5
ldc.i4.s 105
box int32
stelem.ref
call string string::Format(string, object[])
第二个例子
ldstr "{0}one: {1} {2}two: {3} {4}three: {5} {6}four: {7} {8}five: {9} {10}six: {11}"
ldc.i4.s 12
newarr object
dup
ldc.i4.1
ldc.i4.s 100
box int32
stelem.ref
dup
ldc.i4.3
ldc.i4.s 101
box int32
stelem.ref
dup
ldc.i4.5
ldc.i4.s 102
box int32
stelem.ref
dup
ldc.i4.7
ldc.i4.s 103
box int32
stelem.ref
dup
ldc.i4.s 9
ldc.i4.s 104
box int32
stelem.ref
dup
ldc.i4.s 11
ldc.i4.s 105
box int32
stelem.ref
call string string::Format(string, object[])
您必须使用以下方式之一:
string s = @"loooooooooooooooooooooooong loooooong
long long long";
string s = "loooooooooong loooong" +
" long long" ;