string"/>

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

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

Differences in string compare methods in C#

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

1
2
3
4
5
6
string testString ="Test";
string anotherString ="Another";

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

(注:在本例中,我希望平等,不小于或大于,但也可以对此发表评论)


以下是这些函数如何工作的规则:

stringValue.CompareTo(otherStringValue)

  • null在字符串之前
  • 它使用CultureInfo.CurrentCulture.CompareInfo.Compare,这意味着它将使用依赖于文化的比较。这可能意味着?将与德国的SS相比较,或类似。
  • stringValue.Equals(otherStringValue)

  • null不等于任何
  • 除非您指定一个StringComparison选项,否则它将使用一个看起来像是直接序数相等检查的选项,即在任何语言或文化中,?SS不同。
  • stringValue == otherStringValue

  • stringValue.Equals()不同。
  • ==运算符调用静态Equals(string a, string b)方法(然后转到内部EqualsHelper进行比较)。
  • null字符串上调用.Equals()得到null引用异常,而在==上则没有。
  • Object.ReferenceEquals(stringValue, otherStringValue)

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

    请注意,对于上面使用方法调用的选项,有一些重载具有更多的选项来指定如何进行比较。

    我的建议是,如果你只是想检查是否平等,那就决定你是否要使用依赖于文化的比较,然后根据选择使用.CompareTo.Equals


    来自MSDN:

    "The CompareTo method was designed primarily for use in sorting or
    alphabetizing operations. It should not be used when the primary
    purpose of the method call is to determine whether two strings are
    equivalent. To determine whether two strings are equivalent, call
    the Equals method."

    他们建议在寻求平等时使用.Equals而不是.CompareTo。我不确定.Equals==string类中是否存在差异。有时我会用.EqualsObject.ReferenceEquals代替==来处理我自己的类,以防有人在稍后出现并为该类重新定义==运算符。


    如果你对BCL方法的不同很好奇,Reflector就是你的朋友。

    我遵循以下准则:

    完全匹配:edit:I previously always used==operator on the principle that inside equals(string,string)the object==operator is used to compare the object references but it sight stra.equals(strb)is still 1-11%faster overall than string.equals(stra,strb),stra==strb,and string.compareordinal(stra,strb).我用秒表对两个实习/非实习字符串值进行了循环测试,字符串长度相同/不同,大小不同(1b到5Mb)。

    1
    strA.Equals(strB)

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

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

    人类可读匹配(所有其他文化,不敏感的大小写/重音/假名/等由CultureInfo定义):

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

    与自定义规则(所有其他文化)的人类可读匹配:

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

    如Ed所说,compareto用于排序。

    然而,在.equals和==之间有区别。

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

    1
    2
    3
    4
    5
    6
    if(object.ReferenceEquals(left, null) &&
       object.ReferenceEquals(right, null))
        return true;
    if(object.ReferenceEquals(left, null))
        return right.Equals(left);
    return left.Equals(right);

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

    1
    2
    3
    4
    string a = null;
    string b ="foo";

    bool equal = a.Equals(b);

    以下内容将不会:

    1
    2
    3
    4
    string a = null;
    string b ="foo";

    bool equal = a == b;

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

    上述每种方法(和其他方法)都有特定的用途。它们之间的关键区别在于默认情况下它们使用的是哪种StringComparison枚举。有几种选择:

    • 当代文化
    • 当前文化特征酶
    • 不变文化
    • 不变文化特征酶
    • 依次的
    • 顺序排列情况

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

    • 依次的
      • 区分大小写的内部标识符
      • XML和HTTP等标准中区分大小写的标识符
      • 区分大小写的安全相关设置
    • 顺序排列情况
      • 不区分大小写的内部标识符
      • XML和HTTP等标准中不区分大小写的标识符
      • 文件路径(在Microsoft Windows上)
      • 注册表项/值
      • 环境变量
      • 资源标识符(例如,句柄名称)
      • 不区分大小写的安全相关设置
    • 不变文化或不变文化特征酶
      • 一些持续的语言相关数据
      • 显示需要固定排序顺序的语言数据
    • 当前文化或当前文化特征
      • 向用户显示的数据
      • 大多数用户输入

    注意,StringComparison枚举和字符串比较方法的重载自.NET 2.0以来就存在。

    string.compareto方法(string)

    实际上是IComparable.CompareTo方法的类型安全实现。默认解释:当前区域性。

    用途:

    The CompareTo method was designed primarily for use in sorting or alphabetizing operations

    因此

    Implementing the IComparable interface will necessarily use this method

    String.Compare方法

    具有多个重载的字符串类的静态成员。默认解释:当前区域性。

    Whenever possible, you should call an overload of the Compare method that includes a StringComparison parameter.

    String.Equals方法

    从对象类重写并为类型安全而重载。默认解释:序数。注意:

    The String class's equality methods include the static Equals, the static operator ==, and the instance method Equals.

    StringComparer类

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

    You can use the StringComparer class to create a type-specific comparison to sort the elements in a generic collection. Classes such as Hashtable, Dictionary, SortedList, and SortedList use the StringComparer class for sorting purposes.


    通常情况下,99%的用户需要这样做,但如果你必须在一个循环中这样做数百万次,我强烈建议你使用.equals或==因为一旦它发现一个不匹配的字符,它就会将整个事件抛出为false,但如果你使用compareTo,它必须找出字符小于另一个字符,导致性能时间稍差。

    如果您的应用程序将在不同的国家/地区运行,我建议您查看文化信息的含义,并可能使用.equals。因为我只为美国编写应用程序(不在乎别人是否能正常工作),所以我总是使用==。


    在这里列出的表格中,两者没有太大的区别。CompareTo最后调用一个使用当前区域性进行比较的CompareInfo方法;Equals==运算符调用。

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

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


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

    1
    2
    3
    4
    5
    6
    7
    8
           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");

    • s1.compareto(s2):如果主要目的是确定两个字符串是否等效,则不要使用
    • s1==s2:不能忽略大小写
    • s1.equals(s2,StringComparison):如果s1为空,则引发NullReferenceException
    • string.equals(s2,stringComparison):通过消除过程,这个静态方法是胜利者(假设一个典型的用例来确定两个字符串是否相等)!

    使用.equals也很容易阅读。


    使用.equals,还可以获得StringComparison选项。很容易忽视案件和其他事情。

    顺便说一句,这个值将为假

    1
    2
    3
    4
    string a ="myString";
    string b ="myString";

    return a==b

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

    如果将b改为:

    1
    b ="MYSTRING";

    那么a.equals(b)是假的,但是

    1
    a.Equals(b, StringComparison.OrdinalIgnoreCase)

    将是真的

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

    我肯定我会把东西忘了,但我认为如果你需要更多的细节,这应该是足够的信息来开始试验。


    推荐阅读