SequenceEqual 有什么好处?

What is the benefit of SequenceEqual?

我在for循环中做了SequenceEqual和元素比较的比较

static void Main(string[] args)
    {
        //var myList = new List<short>();
        //var anotherList = new List<short>();
        var myList = new short[2000000];
        var anotherList = new short[2000000];

        for (int i = 0; i < 2000000; i++)
        {
            //myList.Add(5);
            //anotherList.Add(5);
            myList[i] = 5;
            anotherList[i] = 5;
        }

        var watch = System.Diagnostics.Stopwatch.StartNew();
        //for (int i = 0; i < 2000000; i++)
        //{
        //    if (myList[i] != anotherList[i])
        //        break;
        //}
        bool isEqual = myList.SequenceEqual(anotherList);
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
        Console.WriteLine(elapsedMs);
        Console.Read();
    }

myList和anotherList为数组时,直接比较执行4ms,SequenceEqual执行21ms。当myList和anotherList为列表时,直接比较执行13ms,SequenceEqual执行30ms

如果慢了那么多,在某些情况下使用它会有好处吗?我只能想到一个,省了好几行代码

更具体地研究 Lists,似乎很多减速来自 SequenceEqual 中使用通用 IEnumerator 而不是 [=11= 的通用实现] 特定版本,速度提高一倍以上。但是,如果您无论如何都要测试 List,不妨直接在 for 循环中编写代码,而不是使用枚举。

请注意 IList 的测试要慢很多,因为 List 没有实现 IList<T>.operator[] 这意味着它调用 IList.operator[] returns object 并导致装箱。

我也是特例 Arrays,因为 IList 界面比直接访问慢得多,同样是由于装箱。

当然,利用可用于已知类型的高速 CountLength 可以使不等长比较比 SequenceEqual 快得多。 OTOH,如果前两个元素不相等,SequenceEqual 可以比我的函数更快。

这是我的 LocalSequenceEqual:

public static bool LocalSequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer = null) {
    if (first is ICollection<TSource> fc && second is ICollection<TSource> sc)
        if (fc.Count != sc.Count)
            return false;

    var cmp = comparer ?? EqualityComparer<TSource>.Default;

    if (first is TSource[] fa && second is TSource[] sa) {
        for (int j1 = 0; j1 < fa.Length; ++j1)
            if (!cmp.Equals(fa[j1], sa[j1]))
                return false;
        return true;
    }

    if (first is List<TSource> fl && second is List<TSource> sl) {
        for (int j1 = 0; j1 < fl.Count; ++j1) {
            if (!cmp.Equals(fl[j1], sl[j1]))
                return false;
        }
        return true;
    }

    using (var e1 = first.GetEnumerator()) {
        using (var e2 = second.GetEnumerator()) {
            while (e1.MoveNext()) {
                if (!(e2.MoveNext() && cmp.Equals(e1.Current, e2.Current)))
                    return false;
            }
            if (e2.MoveNext())
                return false;
        }
    }
    return true;
}