在比较两个字符串及其变量时,我有一个想法:
string str1 = "foofoo";
string strFoo = "foo";
string str2 = strFoo + strFoo;
// Even thought str1 and str2 reference 2 different
//objects the following assertion is true.
Debug.Assert(str1 == str2);
这纯粹是因为.NET运行时识别字符串的值是相同的,并且因为字符串是不可变的,所以str2
的引用等于str1
的引用?
因此,当我们str1 == str2
时,我们实际比较参考和而不是值?我原本以为这是语法糖的产物,但我不正确吗?
我写的任何不准确之处?
答案 0 :(得分:14)
答案在C#Spec§7.10.7
中字符串相等运算符比较字符串值而不是字符串 引用。当两个单独的字符串实例包含完全相同的时 字符序列,字符串的值是相等的,但是 参考文献不同。如§7.10.6中所述,引用类型 可以使用相等运算符来比较字符串引用而不是 字符串值。
答案 1 :(得分:10)
没有
==有效,因为String类将==运算符重载为等于Equals方法。
来自Reflector:
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public static bool operator ==(string a, string b)
{
return Equals(a, b);
}
答案 2 :(得分:7)
实际上,String.Equals
首先检查它是否是相同的引用,如果不是则比较内容。
答案 3 :(得分:7)
如果我们查看jitted代码,我们会看到使用str2
汇编了String.Concat
,并且它实际上与str1
的引用不同。我们还将看到使用Equals
进行比较。换句话说,断言在字符串包含相同字符时传递。
此代码
static void Main(string[] args)
{
string str1 = "foofoo";
string strFoo = "foo";
string str2 = strFoo + strFoo;
Console.WriteLine(str1 == str2);
Debugger.Break();
}
是否适合(请向侧面滚动以查看评论)
C:\dev\sandbox\cs-console\Program.cs @ 22:
00340070 55 push ebp
00340071 8bec mov ebp,esp
00340073 56 push esi
00340074 8b3530206003 mov esi,dword ptr ds:[3602030h] ("foofoo") <-- Note address of "foofoo"
C:\dev\sandbox\cs-console\Program.cs @ 23:
0034007a 8b0d34206003 mov ecx,dword ptr ds:[3602034h] ("foo") <-- Note different address for "foo"
C:\dev\sandbox\cs-console\Program.cs @ 24:
00340080 8bd1 mov edx,ecx
00340082 e81977fe6c call mscorlib_ni+0x2b77a0 (6d3277a0) (System.String.Concat(System.String, System.String), mdToken: 0600035f) <-- Call String.Concat to assemble str2
00340087 8bd0 mov edx,eax
00340089 8bce mov ecx,esi
0034008b e870ebfd6c call mscorlib_ni+0x2aec00 (6d31ec00) (System.String.Equals(System.String, System.String), mdToken: 060002d2) <-- Compare using String.Equals
00340090 0fb6f0 movzx esi,al
00340093 e83870f86c call mscorlib_ni+0x2570d0 (6d2c70d0) (System.Console.get_Out(), mdToken: 060008fd)
00340098 8bc8 mov ecx,eax
0034009a 8bd6 mov edx,esi
0034009c 8b01 mov eax,dword ptr [ecx]
0034009e 8b4038 mov eax,dword ptr [eax+38h]
003400a1 ff5010 call dword ptr [eax+10h]
C:\dev\sandbox\cs-console\Program.cs @ 28:
003400a4 e87775596d call mscorlib_ni+0x867620 (6d8d7620) (System.Diagnostics.Debugger.Break(), mdToken: 0600239a)
C:\dev\sandbox\cs-console\Program.cs @ 29:
>>> 003400a9 5e pop esi
003400aa 5d pop ebp
003400ab c3 ret
答案 4 :(得分:2)
这纯粹是因为.NET运行时识别字符串的值是相同的因为 字符串是不可变的使str2的引用等于str1的引用?
没有。首先,这是因为str1和str2是相同的 - 它们是相同的字符串,因为编译器可以优化它。 strFoo + strFoo是一个编译时间常量,与str1相反。由于字符串在类中是INTERNED,因此它们使用相同的字符串。
其次,字符串OVERRIDES = =方法。查看互联网上可用参考源的源代码一段时间。
答案 5 :(得分:2)
可以覆盖引用相等运算符==
;在System.String
的情况下,它被覆盖以使用值 - 相等行为。对于真正的引用相等,您可以使用Object.ReferenceEquals()
方法,该方法无法覆盖。
答案 6 :(得分:1)
按照代码命中的顺序......
==
被覆盖。这意味着,而不是"abc" == "ab" + "c"
为引用类型(比较引用而不是值)调用默认==
,而是调用string.Equals(a, b)
。
现在,这将执行以下操作:
换句话说,它从以下内容开始:
public static bool ==(string x, string y)
{
//step 1:
if(ReferenceEquals(x, y))
return true;
//step 2:
if(ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
//step 3;
int len = x.Length;
if(len != y.Length)
return false;
//step 4:
for(int i = 0; i != len; ++i)
if(x[i] != y[i])
return false;
return true;
}
除了步骤4是基于指针的版本,其中展开的循环理想情况下应该更快。我不会表明这一点,因为我想谈谈整体逻辑。
有明显的捷径。第一步是在步骤1中。由于相等是自反的(身份需要相等,a == a
)然后我们可以在纳秒内返回true,即使是一个大小为几MB的字符串,如果与自身相比也是如此。
第2步不是捷径,因为它必须进行测试,但请注意,因为我们已经为(string)null == (string)null
返回了true,所以我们不需要另一个分支。因此,呼叫顺序适合快速结果。
第3步允许两件事。它既可以在不同长度的字符串上进行快捷方式(总是假的),也意味着在步骤4中,不会意外地拍摄到其中一个字符串的结尾。
请注意,其他字符串比较不是这种情况,例如WEISSBIER
和weißbier
长度不同,但是大小写不同,因此不区分大小写的比较不能使用步骤3.所有相等比较都可以执行步骤1和2,因为所使用的规则始终有效,所以你应该在你自己使用它们,只有一些可以做第3步。
因此,虽然你建议它是引用而不是比较的值是错误的,但确实首先将引用作为一个非常重要的捷径进行比较。另请注意,实习字符串(通过编译放置在实习池中的字符串或被调用的string.Intern
)将因此经常触发此快捷方式。在您的示例中的代码中就是这种情况,因为编译器在每种情况下都会使用相同的引用。
如果你知道一个字符串被实习,你可以依赖它(只做参考相等测试),但即使你不确定你可以从中受益(参考相等性测试将至少缩短一些当时)。
如果你有一堆字符串,你想要经常测试它们中的一些,但你不想像在实习中那样在内存中延长它们的生命周期,那么你可以使用{{3 }}或XmlNameTable(很快将被重命名为ThreadSafeAtomizer,文档移至LockFreeAtomizer - 应该首先以函数而不是实现细节命名。)
前者由XmlTextReader
内部使用,因此由System.Xml
的其余部分使用,也可由其他代码使用。我写的后者是因为我想要一个类似的想法,对于并发调用,对于不同的类型,以及我可以覆盖相等比较的地方是安全的。
在任何一种情况下,如果您将50个不同的字符串全部放入“abc”中,您将获得一个“abc”引用,允许其他人进行垃圾回收。如果你知道这已经发生了,你可以单独依靠ReferenceEquals
,如果你不确定,那么在情况确实时你仍然可以从捷径中受益。
答案 7 :(得分:0)
根据msdn(http://msdn.microsoft.com/en-us/library/53k8ybth.aspx):
对于预定义的值类型,如果操作数的值相等,则等于运算符(==)返回true,否则返回false。对于除string之外的引用类型,如果其两个操作数引用同一对象,则==返回true。对于字符串类型,==比较字符串的值。