C#中字符串的比较

3

我们知道,对象(在当前例子中为字符串)是相对于它们在堆中的引用进行比较的。所以,如果:

string a = "something"; 
string b = "something"; 
bool isEqual = (a == b);

a的值放入字符串池中,并在搜索池时发现b的值与a相同,那么将为变量b分配相同的引用。好的,这很清楚。但是如果发生以下情况会怎样:

string a = "somethingNew";
bool isEqual = (a == "somethingNew");

这样的比较文字在内存中如何表示(如果有的话),整个比较过程是如何进行的?
4个回答

12

对象可以根据它们在堆中的引用进行比较。如果原生使用这种方式,大多数对象对于人类来说都不容易比较,所以像字符串这样的东西实际上实现了等式操作符的重载,使其更加直观。字符串通过首先检查内存引用(通过调用 object.ReferenceEquals(object, object) )来比较相等性(通过等式 == 操作符),如果引用不同,则退回到比较字符串中的字符,而不考虑内存位置。

诸如“somethingNew”之类的字符串文字被编译为一个变量,该变量与.NET称为国际化内存池中该字符串值的引用相关联...这是一种方式,通过该方式所有具有相同值(即相同大小写和字符)的字符串都作为指向单个引用的指针在国际化内存池中,而不是每个字符串都有自己的内存分配用于相同的值。这样可以节省内存,但要付出在国际化内存池中查找该值的代价。这可行是因为字符串是不可变的(只读的),因此通过使用 + + = 运算符或其他方式改变字符串的值实际上创建了一个全新的字符串。字符串变量默认情况下不会被国际化,除非它们是字符串字面量。

您示例中字符串的比较将在初始字符串等式对象引用检查上成功并返回true,而无需进一步分析相等性。这将发生,因为您的变量是字符串文字,因此已经国际化(具有相同的内存地址)。如果它们没有国际化,比较将再次退回到字符比较,而不考虑内存位置。

您可以通过使用string.Intern(string)手动国际化非文字字符串


2
我意识到所有相同值的字符串将指向相同的引用(包括字符串常量),这意味着 OP 的第二个示例仅检查引用,应该返回 true。要创建相同值的新引用,我认为我们可以使用 string.Copy,然后它将退回比较每个字符,就像你所说的那样。 - King King
1
个人而言,我很惊讶 string.Copy 实际上做了除了返回 this 之外的事情。与之形成对比的是 Clone() (它是 return this;)。有趣! - Marc Gravell
1
@DavidHaney 我被难住了,我想不出这个方法有任何有效的用途;反射器确实显示它被使用了3次;坦白说,我认为这三个都是错误。 - Marc Gravell
1
KingKing实际上更好地为我定义了这个问题。问题的第二部分是关于与常量的比较,而不是字面值,以及这种情况在内存中如何表示(主要是常量在内存中如何表示,并且在进行比较时其值是否在池中被检查)。所以,非常感谢你们两个! - zhulien
@zhulien - 重要的是你得到了答案。拥有多个输入和观点来解决问题是很棒的。 - Haney
显示剩余2条评论

5
因为我们知道,对象(在当前示例中是字符串)是相对于堆中的引用进行比较的。
不正确;==运算符可以被重载,并且确实已经为string类型重载了。
但是如果发生以下情况会发生什么呢?
尽管使用了字符串比较,但即使没有使用字符串比较:因为数据来自字面量(ldstr),所以在这种情况下,由于“interning”,相同的字符串实例将会产生——因此,即使它使用引用比较,它仍然可以工作。

1
这仍然是同样的情况,您不需要为字符串字面值指定变量名称。请注意,字符串覆盖了 operator==(),因此您会得到对字符串内容的比较,而不仅仅是一个普通对象的比较。因此,以下代码同样有效:
 string tail = "New";
 bool isEqual = (a == "something" + tail);

1
字符串的对象引用并不是无关紧要的,因为它首先尝试通过object.ReferenceEquals()检查来“短路”比较。 - Haney
1
你在纠结小事,当然可以优化比较。 - Hans Passant
3
我不同意自己在吹毛求疵。说参考文献不重要是不正确的,如果这是真的,可能会导致一些非常低效的比较(例如,字符串内部池将变得不那么有用)。 - Haney

0

嗯,这让我感到非常困惑,因为我得到的关于这个主题的信息是以一种愚蠢的方式结构化的,首先解释说引用类型之间的比较仅通过使用运算符“==”来比较它们的地址(所有这些都以粗体字发布,并解释了4页)。此外,所有的例子都是用字符串给出的,但是没有一个词是关于它们之间任何值相等的。所以,在这里发布后,我决定完成整个章节的案例,3页后(实际上是最后一页的最后一句话)说明在使用“==”比较字符串时有不同的行为。绝对愚蠢。

所以,只是为了最后检查,确保我已经得到了正确的信息:

在字符串上使用“==”首先检查两个变量是否引用同一个对象。如果不是,则在内容本身上进行实际值比较。

将字符串常量用作:bool isEqual = (a == "somethingNew"); 进行比较,实际上会获取常量值,在所谓的池中搜索它,如果有匹配项,它将放置对同一对象的引用?所以,它实际上被分配为一个变量?抱歉,这对我来说仍然有点不清楚。

最后一个(来自给定文章的示例):

string firstString = "deer";
string secondString = firstString;
string thirdString = "de" + 'e' + 'r';
cw(firstString == secondString); // True - same object
cw(firstString == thirdString); // True - equal objects
cw((object)firstString == (object)secondString); // True
cw((object)firstString == (object)thirdString); //False

在这种情况下,第三个字符串的值不应该在池中搜索,并且整个变量接收到与第一个字符串和第二个字符串相同对象的引用吗?


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