String.Equals(string1.Substring(0,x),string2)是否优于string1.StartsWith(string2)?

时间:2012-01-15 05:21:06

标签: c# .net

我正在使用字符串比较来使用StringComparison.OrdinalIgnoreCase测试网址路径。

MSDN提供以下字符串比较建议HERE,但未澄清为什么

MSDN示例(在上一页的中间):

public static bool IsFileURI(string path) 
{
   path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
   return true;
}

MSDN建议:

“但是,前面的示例使用String.StartsWith(String,StringComparison)方法来测试是否相等。因为比较的目的是测试相等而不是排序字符串,更好的选择是调用Equals方法,如下例所示。“

public static bool IsFileURI(string path)
{
   if (path.Length < 5) return false;

   return String.Equals(path.Substring(0, 5), "FILE:", 
                    StringComparison.OrdinalIgnoreCase);
}

问题:为什么MSDN建议第二个例子更好?

讨论要点:

  1. 显然,第一个示例中的return true;是一个错误,应该是return path.StartsWith(...);我们可以放心地忽略这一点,因为VB代码是正确的。

  2. 在比较相等性之前创建子字符串似乎只使用另一个内存资源,而不仅仅是调用String.StartsWith()。

  3. 长度&lt; 5测试是一个很好的短路,但它可以与之前的代码一样使用。

  4. 第二个例子可以被解释为更清晰的代码,但我关注的是性能。似乎没有必要创建子字符串。

2 个答案:

答案 0 :(得分:4)

使用dotPeek查看StartsWith方法,它最终调用一个比较整个字符串的内部比较函数,并根据该比较的返回值返回一个布尔结果:

return TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0;

String.Equals来电:

return TextInfo.CompareOrdinalIgnoreCase(this, value) == 0;

CompareOrdinalIgnoreCase调用一个私有方法,dotPeek没有显示,但我的预感是StartsWith调用的重载遍历整个字符串,而Equals调用的重载停止为很快就可以确定平等。

如果需要考虑性能问题,请尝试使用适用于您应用的典型值来测量两者。


出于好奇,我尝试测量这两个,看起来Equals明显更快。当我使用发布版本运行下面的代码时,Equals的速度几乎是StartsWith的两倍:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var url = "http://stackoverflow.com/questions/8867710/is-string-equalsstring1-substring0-x-string2-better-than-string1-startswit";
            var count = 10000000;
            var http = false;

            Stopwatch sw = Stopwatch.StartNew();

            for (int i = 0; i < count; i++)
            {
                http = url.StartsWith("http:", StringComparison.OrdinalIgnoreCase);
            }

            sw.Stop();

            Console.WriteLine("StartsWith: {0} ms", sw.ElapsedMilliseconds);

            sw.Restart();

            for (int i = 0; i < count; i++)
            {
                http = string.Equals(url.Substring(0, 5), "http:", StringComparison.OrdinalIgnoreCase);
            }

            sw.Stop();

            Console.WriteLine("Equals: {0} ms", sw.ElapsedMilliseconds);

            Console.ReadLine();
        }
    }
}

答案 1 :(得分:1)

答案位于您在Ordinal String Operations标题下提供的代码示例的正下方。

  

.NET Framework中的字符串可以包含嵌入的空字符。   序数和文化敏感之间最明显的差异之一   比较(包括使用不变文化的比较)   涉及在字符串中处理嵌入的空字符。这些   使用String.Compare和时忽略字符   String.Equals执行区分文化比较的方法   (包括使用不变文化的比较)。结果是,   在文化敏感的比较中,包含嵌入null的字符串   字符可以被认为等于没有的字符串。

     

重要

     

尽管字符串比较方法忽略了嵌入式null   字符串,字符串搜索方法,如String.Contains,   String.EndsWith,String.IndexOf,String.LastIndexOf和   String.StartsWith没有。

意味着使用序数比较的String.StartsWithString.Equals将返回不同的结果。也就是说,使用序数比较的全部目的是防止土耳其系统上的人们绕过安全问题(忽略案例时"f" != "F")。意味着,如果测试使用了"FIL\0E:"

,那么通过传递看起来像String.StartsWith的文件URI,某人仍然可以绕过安全问题