C#中字符串比较方法的差异


261

在C#中比较字符串非常简单。实际上,有几种方法可以做到这一点。我在下面的方框中列出了一些。我很好奇的是它们之间的区别以及何时应该使用它们之间的区别?应该不惜一切代价避免一个吗?我还没有列出更多吗?

string testString = "Test";
string anotherString = "Another";

if (testString.CompareTo(anotherString) == 0) {}
if (testString.Equals(anotherString)) {}
if (testString == anotherString) {}

(注意:我在此示例中寻求的是平等,不小于或大于但也可以对此发表评论)


4
一个陷阱是您不能执行stringValue.Equals(null),因为它假设您可以在null上调用方法
johnc


@RobertHarvey我之所以来stackoverflow是因为我不必阅读多个页面来寻找答案。
Syaiful Nizam Yahya,

@Syaiful:我来Stack Overflow的原因是查找文档中未包含的答案。
罗伯特·哈维

Answers:


231

以下是这些功能如何工作的规则:

stringValue.CompareTo(otherStringValue)

  1. null 在字符串之前
  2. 它使用CultureInfo.CurrentCulture.CompareInfo.Compare,这意味着它将使用与文化相关的比较。这可能意味着ß它将与SS德国或类似国家相比

stringValue.Equals(otherStringValue)

  1. null 不被认为等于任何东西
  2. 除非您指定一个StringComparison选项,否则它将使用看起来像直接序数相等性检查的选项,即ßSS任何语言或文化中的都不相同

stringValue == otherStringValue

  1. 是不一样的stringValue.Equals()
  2. ==运营商调用静态Equals(string a, string b)方法(这反过来又转到内部EqualsHelper做比较。
  3. 调用.Equals()一个null字符串被null引用异常,同时==没有。

Object.ReferenceEquals(stringValue, otherStringValue)

只需检查引用是否相同,即不是只是两个具有相同内容的字符串,而是将一个字符串对象与其自身进行比较。


请注意,使用上述使用方法调用的选项时,会出现重载,其中包含更多选项来指定如何进行比较。

如果您只是想检查是否相等,我的建议是下定决心是否要使用与文化相关的比较,然后根据选择使用.CompareTo.Equals


5
“ stringValue.Equals(otherStringValue):null不等于null”大声笑,我不会。null等于ObjectReferenceNotSet异常。
凯文,

29
==与.Equals()不同... ==运算符调用静态Equals(string a,string b)方法(该方法又转到内部EqualsHelper中进行比较。在null上调用.Equals String获取空引用EXC,而在==没有。
丹C.

2
另一方面,.Equals稍微快一些(内部减少了一个方法调用),但是可读性却很差-当然可以说是:)。
Dan C.

我当时以为'=='将进行引用比较,而object.equals将进行值比较。'=='和string.equals的工作原理相同吗?
amesh 2012年

@ LasseV.Karlsen您对此有何看法String.Compare
JDandChips 2012年

72

从MSDN:

“ CompareTo方法主要设计用于排序或按字母顺序排序。当方法调用的主要目的是确定两个字符串是否等效时,不应使用此方法。要确定两个字符串是否等效,请调用Equals方法。 ”

他们建议在单独寻找平等时使用.Equals而不是.CompareTo。我不知道是否有之间的差异.Equals,并==string类。有时我会使用.EqualsObject.ReferenceEquals代替==我自己的类,以防某人稍后出现并重新定义==该类的运算符。


18
你有没有想过 (重新定义==)......我认为这是waaaay太防御性编程=)
胡安

是的,这就是为什么我现在在寻找对象相等性时使用Object.ReferenceEquals :)。这可能有点过分的防守,但是我对此并不疯狂,而且这种情况确实不会经常出现。
Ed S.

我怀疑这种“防御性编码”是否有用。如果类所有者需要重写==运算符,然后发现没有人在使用它,该怎么办?
Dave Van den Eynde

1
@DaveVandenEynde:是的。我不定期执行此操作,仅覆盖优先级。
Ed S.

1
Microsoft的建议记录在这里:在.NET Framework中使用字符串的最佳实践
JJS 2013年

50

如果您对BCL方法的差异感到好奇,那么Reflector是您的朋友:-)

我遵循以下准则:

完全匹配: 编辑:我以前总是使用==运算符,其原则是在Equals(string,string)内部,对象==运算符用于比较对象引用,但看起来strA.Equals(strB)仍为1-11%总体上比string.Equals(strA,strB),strA == strB和string.CompareOrdinal(strA,strB)更快。我使用秒表对已插入/未插入的字符串值(具有相同/不同的字符串长度和大小)(1B至5MB)进行了循环测试。

strA.Equals(strB)

人类可读的匹配(西方文化,不区分大小写):

string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) == 0

人类可读的匹配(所有其他区域性,CultureInfo定义的不区分大小写/重音/假名/等):

string.Compare(strA, strB, myCultureInfo) == 0

易于理解的匹配与自定义规则(所有其他区域性):

CompareOptions compareOptions = CompareOptions.IgnoreCase
                              | CompareOptions.IgnoreWidth
                              | CompareOptions.IgnoreNonSpace;
string.Compare(strA, strB, CultureInfo.CurrentCulture, compareOptions) == 0

18

正如Ed所说,CompareTo用于排序。

但是,.Equals和==之间是有区别的。

== 本质上解析为以下代码:

if(object.ReferenceEquals(left, null) && 
   object.ReferenceEquals(right, null))
    return true;
if(object.ReferenceEquals(left, null))
    return right.Equals(left);
return left.Equals(right);

原因很简单,以下将引发异常:

string a = null;
string b = "foo";

bool equal = a.Equals(b);

并且以下内容不会:

string a = null;
string b = "foo";

bool equal = a == b;

15

有关字符串比较问题的良好解释和实践,可以在Microsoft .NET 2.0中使用字符串的新建议以及.NET Framework中使用字符串的最佳实践中找到


每种提到的方法(和其他方法)都有特定的用途。它们之间的主要区别是它们默认使用哪种StringComparison枚举。有几种选择:

  • 当前文化
  • 当前文化忽略案例
  • 不变文化
  • 不变文化忽略案例
  • 序数
  • 顺序忽略情况

以上每种比较类型都针对不同的用例:

  • 序数
    • 区分大小写的内部标识符
    • XML和HTTP等标准中区分大小写的标识符
    • 区分大小写的安全相关设置
  • 顺序忽略情况
    • 不区分大小写的内部标识符
    • XML和HTTP等标准中不区分大小写的标识符
    • 文件路径(在Microsoft Windows上)
    • 注册表项/值
    • 环境变量
    • 资源标识符(例如句柄名称)
    • 不区分大小写的安全性相关设置
  • InvariantCulture或InvariantCultureIgnoreCase
    • 一些持久的语言相关数据
    • 显示需要固定排序顺序的语言数据
  • CurrentCulture或CurrentCultureIgnoreCase
    • 显示给用户的数据
    • 大多数用户输入

请注意,自.NET 2.0开始,存在StringComparison枚举以及字符串比较方法的重载。


String.CompareTo方法(字符串)

实际上是IComparable.CompareTo方法的类型安全实现。默认解释:CurrentCulture。

用法:

CompareTo方法主要设计用于排序或按字母顺序排列的操作

从而

实现IComparable接口将必然使用此方法

String.Compare方法

具有许多重载的String类的静态成员。默认解释:CurrentCulture。

只要有可能,就应该调用包含StringComparison参数的Compare方法的重载。

String.Equals方法

从Object类重写,并为确保类型安全而重载。默认解释:序数。注意:

String类的equals方法包括静态Equals静态运算符==实例方法Equals


StringComparer类

还有另一种处理字符串比较的方法,特别是针对排序的:

您可以使用StringComparer类创建特定于类型的比较,以对通用集合中的元素进行排序。Hashtable,Dictionary,SortedList和SortedList之类的类使用StringComparer类进行排序。


2
根据有关SO的其他一些帖子,除序数方法外,所有方法都存在Compare(a,b)和Compare(b,a)都可以返回1的情况,并且该错误已归类为“无法修复” ”。因此,我不知道任何这样的比较有任何使用情况。
超级猫

您可以链接@supercat还是举个例子?
Noctis 2014年

1
有关该问题的讨论,请参见stackoverflow.com/questions/17599084/…
超级猫2014年

7

并不是说性能通常不会影响您需要执行此操作的99%的时间,但是如果您必须循环执行几百万次,我强烈建议您使用.Equals或==,因为一旦找到字符如果不匹配,则会将整个结果视为错误,但如果使用CompareTo,则必须找出哪个字符比另一个字符少,从而导致性能时间稍差。

如果您的应用程序将在不同的国家/地区运行,建议您看看CultureInfo的含义,并可能使用.Equals。由于我只为美国真正编写应用程序(并且不在乎是否有人无法正常工作),所以我总是只使用==。


5

在此处列出的表格中,两者之间没有太大区别。 CompareTo最终调用了CompareInfo一种使用当前区域性进行比较的方法;Equals==操作员调用。

如果考虑过载,情况会有所不同。 Compare并且==只能使用当前区域性来比较字符串。 Equals并且String.Compare可以采用StringComparison枚举参数,该参数使您可以指定不区分区域性或不区分大小写的比较。仅String.Compare允许您CultureInfo使用默认区域性以外的区域性指定a 并执行比较。

由于它的多功能性,我发现我使用的String.Compare比其他任何比较方法都多;它让我确切地指定我想要的。


2

要注意的一个大区别是。如果第一个字符串为null,则Equals()将引发异常,而==则不会。

       string s = null;
        string a = "a";
        //Throws {"Object reference not set to an instance of an object."}
        if (s.Equals(a))
            Console.WriteLine("s is equal to a");
        //no Exception
        if(s==a)
            Console.WriteLine("s is equal to a");

0
  • s1.CompareTo(s2): 如果主要目的是确定两个字符串是否等效,则不要使用
  • s1 == s2: 无法忽略大小写
  • s1.Equals(s2,StringComparison): 如果s1为null,则抛出NullReferenceException
  • String.Equals(s2,StringComparison): 通过消除过程,此静态方法就是WINNER(假设一个典型的用例来确定两个字符串是否等效)!


-9

使用.Equals,您还将获得StringComparison选项。对于忽略大小写和其他内容非常方便。

顺便说一句,这将评估为false

string a = "myString";
string b = "myString";

return a==b

因为==比较a和b的值(它们是指针),所以只有当指针指向内存中的同一对象时,这才算为true。.Equals取消引用指针,并比较存储在指针处的值。a。等于(b)在这里是正确的。

并且如果将b更改为:

b = "MYSTRING";

那么a.Equals(b)为假,但是

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

会是真的

a.CompareTo(b)调用字符串的CompareTo函数,该函数比较指针处的值,如果存储在a处的值小于存储在b处的值,则返回<0;如果a.Equals(b)为true,则返回0。否则> 0。但是,这是区分大小写的,我认为CompareTo可能有忽略大小写之类的选项,但是现在没有时间查看。正如其他人已经指出的那样,这将用于排序。以这种方式进行平等比较将导致不必要的开销。

我确定我会遗漏任何东西,但是如果您需要更多详细信息,我认为这应该足够开始尝试的信息。


9
a == b部分不正确。对于String类,==运算符实际上已重载,并且它比较值而不管实际引用是什么。
Goyuix 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.