C#中字符串比较方法的区别

284

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

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

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

(注意:在此示例中,我寻求平等而非小于或大于,但您也可以随意评论)


5
一个陷阱是你不能调用 stringValue.Equals(null),因为这将假定你可以在 null 上调用方法。 - johnc
1
MSDN 参考文献 - Robert Harvey
1
@RobertHarvey 我来到Stack Overflow的原因是为了不必阅读多个页面以获取答案。 - Syaiful Nizam Yahya
@Syaiful:我来到Stack Overflow的原因是寻找那些文档中没有的答案。 - Robert Harvey
11个回答

242
以下是这些功能的工作规则: 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. null 字符串调用 .Equals() 会得到 null 引用异常,但对 == 不会。
Object.ReferenceEquals(stringValue, otherStringValue) 仅检查引用是否相同,即它不仅仅是具有相同内容的两个字符串,而是将一个字符串对象与自身进行比较。
请注意,对于上述使用方法调用的选项,还有更多的重载选项可指定如何进行比较。
如果您只想检查相等性,建议您确定您是否要使用与文化相关的比较,然后根据选择使用 .CompareTo.Equals

6
"stringValue.Equals(otherStringValue): null is not equal to null" 翻译为: "stringValue.Equals(otherStringValue):null 不等于 null。" "Lol, I'd say not. null equals ObjectReferenceNotSet exception." 翻译为:"哈哈,我不这么认为。null 等同于 ObjectReferenceNotSet 异常。" - Kevin
32
"== 不等于 .Equals()... == 运算符会调用静态的 Equals(string a, string b) 方法(该方法又会进入内部的 EqualsHelper 做比较)。对空字符串调用 .Equals 会得到 null 引用异常,但对 == 操作符不会。" - Dan C.
2
另一方面,.Equals 稍微快一些(内部少了一个方法调用),但可读性较差 - 当然,这是有争议的 :)。 - Dan C.
我曾认为'=='会进行引用比较,而object.equals会进行值比较。'=='和string.equals如何起到相同的作用? - amesh
答案中应该指出“==”的区别,这是一个相当大的区别。 - Joe Cartano
你能为每个添加大小写敏感吗? - serge

76

来自MSDN:

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

他们建议在仅寻找相等性时使用.Equals而不是.CompareTo。我不确定对于string类,.Equals和==之间是否有区别。对于我的自定义类,有时我会使用.Equals或Object.ReferenceEquals而不是==,以防以后有人重新定义了该类的==运算符。


22
发生过这种情况吗?(重新定义 == )...我认为这是过于防御性的编程 =) - juan
1
@DaveVandenEynde:是的...我之前写过这个。我不会经常这样做,只在适当的时候重写.Equals。 - Ed S.
1
微软的建议记录在这里:在.NET Framework中使用字符串的最佳实践 - JJS
这个“答案”并没有回答问题。 - sisharp
如何防范 '==' 的行为建议,实际上引出了如何处理 '+'、'-' 或 '.' 的问题。请参考此处的一个好案例:https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition/blob/uinverse/src/main/java/com/seriouscompany/business/java/fizzbuzz/packagenamingpackage/impl/strategies/comparators/integercomparator/IntegerForEqualityComparator.java - Elliott Beach
显示剩余2条评论

50

如果您对BCL方法的差异感到好奇,Reflector可能会很有帮助 :-)

我遵循以下准则:

完全匹配:编辑:我之前总是使用==运算符,因为在Equals(string, string)内部使用对象的==运算符来比较对象引用,但似乎strA.Equals(strB)总体上仍然比string.Equals(strA, strB)、strA == strB和string.CompareOrdinal(strA, strB)快1-11%,我使用了StopWatch在interned/non-interned字符串值上进行了循环测试,具有相同/不同的字符串长度和不同的大小(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

20

就像 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;

大小写敏感性怎么样? - serge
并备注您可以使用string.Equals(a,b) - serge

15

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


每种方法(以及其他方法)都有特定的目的。它们之间的关键区别在于它们默认使用什么样的StringComparison Enumeration。有几个选项:
  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • Ordinal
  • OrdinalIgnoreCase
上述每种比较类型都针对不同的用例。
  • Ordinal
    • 区分大小写的内部标识符
    • 区分大小写的标准标识符,如XML和HTTP
    • 与安全相关的区分大小写的设置
  • OrdinalIgnoreCase
    • 不区分大小写的内部标识符
    • 不区分大小写的标准标识符,如XML和HTTP
    • 文件路径(在Microsoft Windows上)
    • 注册表键/值
    • 环境变量
    • 资源标识符(例如句柄名称)
    • 与安全相关的不区分大小写的设置
  • InvariantCulture或InvariantCultureIgnoreCase
    • 一些持久化的与语言相关的数据
    • 需要固定排序顺序的语言数据的显示
  • CurrentCulture或CurrentCultureIgnoreCase
    • 向用户显示的数据
    • 大多数用户输入

请注意,自.NET 2.0以来,StringComparison Enumeration以及字符串比较方法的重载都已存在。


String.CompareTo方法(String)

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

用法:

CompareTo方法主要用于排序或字母顺序操作

因此

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

String.Compare方法

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

尽可能调用包含StringComparison参数的Compare方法的重载。

String.Equals方法

从Object类重载并为类型安全覆盖。默认解释:序数。

String类的相等性方法包括静态Equals、静态operator ==和实例方法Equals。


StringComparer类

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

您可以使用StringComparer类创建一个类型特定的比较器,以便在通用集合中对元素进行排序。Hashtable、Dictionary、SortedList和SortedList等类使用StringComparer类进行排序。


2
根据 Stack Overflow 上的其他帖子,除了序数方法之外的所有方法都存在 Compare(a,b) 和 Compare(b,a) 都可能返回 1 的情况,而且这个 bug 被归类为“不会修复”。因此,我不确定任何这样的比较都有任何用例。 - supercat
@supercat,您可以提供链接或举个例子吗? - Noctis
1
请参考以下讨论了解此问题:https://dev59.com/CXTYa4cB1Zd3GeqPtlvN - supercat

7

虽然在大多数情况下性能并不重要,但如果您需要在循环中执行此操作数百万次,我强烈建议您使用.Equals或==,因为一旦找到不匹配的字符,它就会将整个内容标记为false。但是,如果您使用CompareTo,它将不得不确定哪个字符小于另一个字符,导致稍微劣化的性能时间。

如果您的应用程序将在不同的国家/地区运行,我建议您查看CultureInfo的影响,并可能使用.Equals。由于我只编写面向美国的应用程序(并且不关心它是否能正常工作),我总是使用==。


5
在这里列出的表单中,两者之间没有太大区别。`CompareTo`最终会调用一个`CompareInfo`方法,使用当前文化来进行比较;`Equals`则由`==`运算符调用。
如果考虑重载,那么情况就不同了。`Compare`和`==`只能使用当前文化来比较字符串。`Equals`和`String.Compare`可以接受一个`StringComparison`枚举参数,让您指定无关文化或不区分大小写的比较。只有`String.Compare`允许您指定`CultureInfo`并使用除默认文化之外的文化进行比较。
因为其多功能性,我发现自己比其他任何比较方法更常使用`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");

1
  • s1.CompareTo(s2): 如果主要目的是确定两个字符串是否相等,则不要使用
  • s1 == s2: 不能忽略大小写
  • s1.Equals(s2, StringComparison): 如果s1为null,则会抛出NullReferenceException
  • String.Equals(s2, StringComparison): 经过排除,这个 静态 方法是胜者(假设典型用例是确定两个字符串是否相等)!

-1

使用.Equals 也更容易阅读


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