字符串连接和引用相等
string concatenation and reference equality
在 C# 中,字符串是不可变的和托管的。从理论上讲,这意味着任何字符串 A
和 B
的连接都会导致新缓冲区的分配,但这一切都非常混乱。当您连接标识(空字符串)时,引用保持不变。这是编译时优化还是重载赋值运算符决定在运行时不重新分配?另外,当我修改s1
的值时,runtime/compiler如何处理s2
的value/allocation?我的程序将指示 s1
原始地址处的内存保持不变(并且 s2
继续指向那里),同时为新值发生重新分配,然后 s1
指向那里,是这是对幕后发生的事情的准确描述?
示例程序;
static void Main(string[] args)
{
string s1 = "Some random text I chose";
string s2 = s1;
string s3 = s2;
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // true
s1 = s1 + "";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // true
Console.WriteLine(s2);
s1 = s1 + " something else";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // false cause s1 got realloc'd
Console.WriteLine(Object.ReferenceEquals(s2, s3));
Console.WriteLine(s2);
Console.ReadKey();
}
When you concatenate with the identity (the empty string) the reference maintains intact. Is this a compile time optimization or is the overloaded assignment operator making the decision to not realloc at runtime?
既是编译时的优化,也是在重载的concatenation运算符的实现中进行的优化。如果您连接两个编译时文字,或连接一个在编译时已知为 null 或空的字符串,则连接在编译时完成,然后可能会被保留,因此将被引用等于任何其他编译时文字字符串相同的值。
此外,String.Concat
的实现方式是,如果您使用 null
或空字符串连接一个字符串,它只会 returns 另一个字符串(除非另一个字符串是 null
,在这种情况下它 returns 一个空字符串)。您已经进行的测试证明了这一点,因为您将非编译时文字字符串与空字符串连接起来并且它保持引用相等。
当然,如果您不相信自己的测试,您可以 look at the source 看看如果其中一个参数为空,那么它只是 returns 另一个。
if (IsNullOrEmpty(str0)) {
if (IsNullOrEmpty(str1)) {
return String.Empty;
}
return str1;
}
if (IsNullOrEmpty(str1)) {
return str0;
}
When you concatenate with the identity (the empty string) the reference maintains intact. Is this a compile time optimization or is the overloaded assignment operator making the decision to not realloc at runtime?
这是 运行 次优化。这是它在 Mono 中的实现方式:
public static String Concat(String str0, String str1) {
Contract.Ensures(Contract.Result() != null);
Contract.Ensures(Contract.Result().Length ==
(str0 == null ? 0 : str0.Length) +
(str1 == null ? 0 : str1.Length));
Contract.EndContractBlock();
// ========= OPTIMIZATION BEGINS ===============
if (IsNullOrEmpty(str0)) {
if (IsNullOrEmpty(str1)) {
return String.Empty;
}
return str1;
}
if (IsNullOrEmpty(str1)) {
return str0;
}
// ========== OPTIMIZATION ENDS =============
int str0Length = str0.Length;
String result = FastAllocateString(str0Length + str1.Length);
FillStringChecked(result, 0, str0);
FillStringChecked(result, str0Length, str1);
return result;
}
编译器可能会产生自己的额外优化 - 例如,连接两个字符串文字会在编译时产生一个新的文字值,而无需调用 string.Concat
。不过,这与 C# 处理包含其他数据类型的编译时常量的其他表达式没有什么不同。
Furthermore, how does the runtime/compiler handle s2
's value/allocation when I modify the value of s1
?
s1
和 s2
是对同一个 string
对象的独立引用,它是不可变的。将另一个对象重新分配给其中一个对象不会更改另一个引用。
When you concatenate with the identity (the empty string) the
reference maintains intact. Is this a compile time optimization or is
the overloaded assignment operator making the decision to not realloc
at runtime?
都没有。做出该决定的是 Concat
方法。代码实际编译成:
s1 = String.Concat(s1, "");
Concat
方法包含此代码,如果第二个参数为空,则 return 成为第一个参数:
if (IsNullOrEmpty(str1)) {
return str0;
}
参考:Microsoft reference source: String.Concat(string, string)
My program would indicate that the memory at the original address of
s1 remains intact (and s2 continues pointing there) while a relloc
occurs for the new value and then s1 is pointed there
没错。
String.Concat 函数决定不连接字符串。它检查 s1 是否为 null,如果是,则将 "" 分配给 s1。
s1 = s1 + "";
由编译器优化。
s1 = s1 ?? "";
如果您想了解更多信息,请查看 this link
字符串连接被指定为return一个字符串,其字符序列是被连接的事物的字符串表示所封装的序列的连接。如果现有字符串不包含正确的字符序列,连接代码将需要创建一个新字符串;此外,即使在现有字符串可能包含正确字符序列的情况下,计算机创建新字符串通常比尝试查找现有字符串更快。但是,我相信,在任何情况下都可以将现有字符串 return 连接起来,因为它可以快速找到包含正确字符的字符串,并且在将零长度字符串连接到非零长度字符串的情况下length string,找到包含正确字符的字符串很容易。
由于上述行为细节,在大多数情况下,ReferenceEquals
与字符串的唯一合法应用是在 true
结果被解释为 "the strings definitely contain the same characters" 和"false" 结果说 "the strings might not contain the same characters"。它不应被解释为说明字符串的来源、创建方式或类似内容。
在 C# 中,字符串是不可变的和托管的。从理论上讲,这意味着任何字符串 A
和 B
的连接都会导致新缓冲区的分配,但这一切都非常混乱。当您连接标识(空字符串)时,引用保持不变。这是编译时优化还是重载赋值运算符决定在运行时不重新分配?另外,当我修改s1
的值时,runtime/compiler如何处理s2
的value/allocation?我的程序将指示 s1
原始地址处的内存保持不变(并且 s2
继续指向那里),同时为新值发生重新分配,然后 s1
指向那里,是这是对幕后发生的事情的准确描述?
示例程序;
static void Main(string[] args)
{
string s1 = "Some random text I chose";
string s2 = s1;
string s3 = s2;
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // true
s1 = s1 + "";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // true
Console.WriteLine(s2);
s1 = s1 + " something else";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // false cause s1 got realloc'd
Console.WriteLine(Object.ReferenceEquals(s2, s3));
Console.WriteLine(s2);
Console.ReadKey();
}
When you concatenate with the identity (the empty string) the reference maintains intact. Is this a compile time optimization or is the overloaded assignment operator making the decision to not realloc at runtime?
既是编译时的优化,也是在重载的concatenation运算符的实现中进行的优化。如果您连接两个编译时文字,或连接一个在编译时已知为 null 或空的字符串,则连接在编译时完成,然后可能会被保留,因此将被引用等于任何其他编译时文字字符串相同的值。
此外,String.Concat
的实现方式是,如果您使用 null
或空字符串连接一个字符串,它只会 returns 另一个字符串(除非另一个字符串是 null
,在这种情况下它 returns 一个空字符串)。您已经进行的测试证明了这一点,因为您将非编译时文字字符串与空字符串连接起来并且它保持引用相等。
当然,如果您不相信自己的测试,您可以 look at the source 看看如果其中一个参数为空,那么它只是 returns 另一个。
if (IsNullOrEmpty(str0)) {
if (IsNullOrEmpty(str1)) {
return String.Empty;
}
return str1;
}
if (IsNullOrEmpty(str1)) {
return str0;
}
When you concatenate with the identity (the empty string) the reference maintains intact. Is this a compile time optimization or is the overloaded assignment operator making the decision to not realloc at runtime?
这是 运行 次优化。这是它在 Mono 中的实现方式:
public static String Concat(String str0, String str1) {
Contract.Ensures(Contract.Result() != null);
Contract.Ensures(Contract.Result().Length ==
(str0 == null ? 0 : str0.Length) +
(str1 == null ? 0 : str1.Length));
Contract.EndContractBlock();
// ========= OPTIMIZATION BEGINS ===============
if (IsNullOrEmpty(str0)) {
if (IsNullOrEmpty(str1)) {
return String.Empty;
}
return str1;
}
if (IsNullOrEmpty(str1)) {
return str0;
}
// ========== OPTIMIZATION ENDS =============
int str0Length = str0.Length;
String result = FastAllocateString(str0Length + str1.Length);
FillStringChecked(result, 0, str0);
FillStringChecked(result, str0Length, str1);
return result;
}
编译器可能会产生自己的额外优化 - 例如,连接两个字符串文字会在编译时产生一个新的文字值,而无需调用 string.Concat
。不过,这与 C# 处理包含其他数据类型的编译时常量的其他表达式没有什么不同。
Furthermore, how does the runtime/compiler handle
s2
's value/allocation when I modify the value ofs1
?
s1
和 s2
是对同一个 string
对象的独立引用,它是不可变的。将另一个对象重新分配给其中一个对象不会更改另一个引用。
When you concatenate with the identity (the empty string) the reference maintains intact. Is this a compile time optimization or is the overloaded assignment operator making the decision to not realloc at runtime?
都没有。做出该决定的是 Concat
方法。代码实际编译成:
s1 = String.Concat(s1, "");
Concat
方法包含此代码,如果第二个参数为空,则 return 成为第一个参数:
if (IsNullOrEmpty(str1)) {
return str0;
}
参考:Microsoft reference source: String.Concat(string, string)
My program would indicate that the memory at the original address of s1 remains intact (and s2 continues pointing there) while a relloc occurs for the new value and then s1 is pointed there
没错。
String.Concat 函数决定不连接字符串。它检查 s1 是否为 null,如果是,则将 "" 分配给 s1。
s1 = s1 + "";
由编译器优化。
s1 = s1 ?? "";
如果您想了解更多信息,请查看 this link
字符串连接被指定为return一个字符串,其字符序列是被连接的事物的字符串表示所封装的序列的连接。如果现有字符串不包含正确的字符序列,连接代码将需要创建一个新字符串;此外,即使在现有字符串可能包含正确字符序列的情况下,计算机创建新字符串通常比尝试查找现有字符串更快。但是,我相信,在任何情况下都可以将现有字符串 return 连接起来,因为它可以快速找到包含正确字符的字符串,并且在将零长度字符串连接到非零长度字符串的情况下length string,找到包含正确字符的字符串很容易。
由于上述行为细节,在大多数情况下,ReferenceEquals
与字符串的唯一合法应用是在 true
结果被解释为 "the strings definitely contain the same characters" 和"false" 结果说 "the strings might not contain the same characters"。它不应被解释为说明字符串的来源、创建方式或类似内容。