检查两个列表是否相等

276

我有一个如下的类:

public class Tag {
    public Int32 Id { get; set; }
    public String Name { get; set; }
}

我有两个标签列表:

List<Tag> tags1;
List<Tag> tags2;

我使用 LINQ 的 select 方法来获取每个标签列表的 Id。然后:
List<Int32> ids1 = new List<Int32> { 1, 2, 3, 4 };
List<Int32> ids2 = new List<Int32> { 1, 2, 3, 4 };
List<Int32> ids3 = new List<Int32> { 2, 1, 3, 4 };
List<Int32> ids4 = new List<Int32> { 1, 2, 3, 5 };
List<Int32> ids5 = new List<Int32> { 1, 1, 3, 4 };

ids1应该等于ids2和ids3...两者具有相同的数字。

ids1不应该等于ids4和ids5...

我尝试了以下方法:

var a = ints1.Equals(ints2);
var b = ints1.Equals(ints3);

但是两者都给了我错误的结果。

检查标签列表是否相等的最快方式是什么?

更新

我正在寻找标签完全与书中相同的POSTS。

IRepository repository = new Repository(new Context());

IList<Tags> tags = new List<Tag> { new Tag { Id = 1 }, new Tag { Id = 2 } };

Book book = new Book { Tags = new List<Tag> { new Tag { Id = 1 }, new Tag { Id = 2 } } };

var posts = repository
  .Include<Post>(x => x.Tags)
  .Where(x => new HashSet<Int32>(tags.Select(y => y.Id)).SetEquals(book.Tags.Select(y => y.Id)))
  .ToList();

我正在使用 Entity Framework,但是出现了错误:

类型为 'System.NotSupportedException' 的异常在 mscorlib.dll 中发生,但未在用户代码中处理

更多信息:LINQ to Entities 不识别方法 'Boolean SetEquals(System.Collections.Generic.IEnumerable`1[System.Int32])',而且此方法无法转换为存储表达式。

如何解决这个问题?


你所说的“不完全相等”是什么意思?是指所有元素都应该不同,还是只是不能包含相同的元素,至少应该有一个不同? - Selman Genç
你的序列 ids5 包含重复项。这是有意为之吗? - Sergey Kalinichenko
1
@ Selman22 我的意思是这两个列表应该包含完全相同的元素...顺序无所谓。 - Miguel Moura
@dasblinkenlight 是的,在这种情况下,ID是唯一的,因为它们是主键,所以没有意义。 - Miguel Moura
由于您的编辑会导致问题的解决方案与目前已发布的任何内容完全不同,因此您可能需要单独发布更新后的问题。请添加[EF]标签,并确保新问题的标题为“在EF的Where子句内部比较列表”或类似内容。 - Sergey Kalinichenko
显示剩余3条评论
5个回答

520

使用 SequenceEqual 来检查序列相等性,因为 Equals 方法检查的是引用相等性

var a = ints1.SequenceEqual(ints2);

如果您不关心元素的顺序,请使用Enumerable.All方法:

var a = ints1.All(ints2.Contains);

第二个版本还需要检查 Count,因为即使 ints2 包含比 ints1 更多的元素,它也会返回 true。因此,更正确的版本应该像这样:

var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count;
为了检查不等式,只需反转All方法的结果:

反转All方法的结果即可检查不等式:

var a = !ints1.All(ints2.Contains)

18
慢且无法处理重复项。[1, 1, 2] != [1, 2, 2] - CodesInChaos
2
@CodesInChaos 根据问题中的评论,重复项并不重要。 - Selman Genç
23
注意:你可能习惯于使用像.All(i=> ints2.Contains(i))这样的lambda表达式与.All一起使用,但由于list.Contains()匹配了接受int并返回bool的函数签名,所以他直接将函数名作为谓词传递。本质上与ints1.All(i=> ints2.Contains(i))相同。只是想指出这一点,以防其他像我一样的人最初感到困惑。 - AaronLS
46
我会翻译为:我建议将var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count;的顺序改成var a = ints1.Count == ints2.Count && ints1.All(ints2.Contains);。仅进行简单数量比较可能比使用.All更快。如果数量不相等,它会更快地返回。 - Spiralis
12
我会下投票反对这个答案,因为它并不适用于所有情况。列表A {a,a} 和列表B包含{b,a},现在 ListB.All(listA.contains) 和 ListA.All(listB.contains) 将给出不同的结果,因为两者计数相同,我们将在其中一个中得到 true,即使两者都不同。如果列表有多个条目,它将无效。 - Pratyush Dhanuka
显示剩余10条评论

154

List<T> 的相等性不是逐个元素进行检查的。您可以使用 LINQ 的 SequenceEqual 方法 进行检查:

var a = ints1.SequenceEqual(ints2);

要忽略顺序,请使用SetEquals

var a = new HashSet<int>(ints1).SetEquals(ints2);

这应该有效,因为您在比较不包含重复项的ID序列。如果有重复项,并且您需要考虑重复项,则以线性时间完成的方法是组成一个基于哈希的计数字典,在第一个序列的每个元素上加1,在第二个序列的每个元素上减1,并检查结果计数是否全部为零:
var counts = ints1
    .GroupBy(v => v)
    .ToDictionary(g => g.Key, g => g.Count());
var ok = true;
foreach (var n in ints2) {
    int c;
    if (counts.TryGetValue(n, out c)) {
        counts[n] = c-1;
    } else {
        ok = false;
        break;
    }
}
var res = ok && counts.Values.All(c => c == 0);

最后,如果您可以接受 O(N*LogN) 解决方案,则可以对这两个序列进行排序,并使用 SequenceEqual 进行比较以确认相等性。


1
使用 SequenceEqual 方法需要元素按照相同的顺序排列 - 但 OP 想要无论元素顺序如何都可以比较相等。 - D Stanley
3
那正是编辑的作用。编辑后看起来怎么样? - Sergey Kalinichenko
1
@dasblinkenlight仍然无法工作,如果任何一个序列包含重复项 - {1, 1, 2, 3}将与{1, 2, 2, 3}“相等”。 - D Stanley
1
@dasblinkenlight:也许像这样的代码可以运行:bool isEqual = ids1.Count == ids2.Count; if (isEqual) isEqual = ids1.OrderBy(i=>i).SequenceEqual(ids2.OrderBy(i => i));. - Tim Schmelter
2
@dasblinkenlight 当然可以。事实上,楼主示例中的第五个序列就是这样做到的。 - D Stanley
显示剩余8条评论

29
Enumerable.SequenceEqual(FirstList.OrderBy(fElement => fElement), 
                         SecondList.OrderBy(sElement => sElement))

9
你的 lambda 参数名称很奇怪。它们不是列表,而是元素。在 OP 的上下文中,我建议使用 id,在通用情况下则使用 element - CodesInChaos
这个人显然是从别处抄来的答案,因为根据参数名称判断,他甚至不知道它的作用是什么。 - Mark Silver
1
我认为它恰好涵盖了手头的问题。它比较两个列表,无论顺序如何。 - Rob Vermeulen
如果您想比较并忽略重复项,甚至可以在 OrderBy 之前添加 .Distinct()。这绝对不是比较集合的最有效方法。HashSet.SetEquals 忽略重复项和顺序。Enumerable.SequenceEquals 考虑重复项并保留顺序。 - Triynko

0
近10年来(Selman的回答)被接受的答案根本不起作用。你可以自己尝试一下:{2, 2, 3}和{2, 3, 3},Selman算法说它们相等。我同意一开始并不明显。
第二个得票最多的答案(Sergey的答案)包含了一些评论,建议了一个非常快速的算法。我进行了一次小型基准测试(代码在底部)。我包括了3个答案(Selman、Sergey、Pankaj)。我还添加了2个新算法(Guru和EricO)。根据我的基准测试,Sergey的算法与其他一些算法相比并不是很快。我知道速度本身并不是问题的标准,但答案提出了一个非常高效的算法。
在这里,我包括了一个小型基准测试来比较提出的解决方案以及其他2个解决方案。虽然远非完美和全面,但这个基准测试可以给出一个关于有效性和性能的好主意。我建议在选择要在自己的代码中实现的内容之前,先检查一下结果。
测试代码和比较函数的实现在结果下方。感谢Guru对算法的反馈和好的想法(我认为它值得成为一个独立的答案;-))。
结果:
Start Compare for Int32 of size: 3
CompareEnumerableAnyOrderSelman    : 00:00:00.0006856. Equal: True  <<<<< ERROR
CompareEnumerableAnyOrderSergey    : 00:00:00.0045392. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:00.0019844. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:00.0002651. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:00.0005945. Equal: False
End Compare

Start Compare for Int32 of size: 10000000
CompareEnumerableAnyOrderSergey    : 00:00:11.6260563. Equal: True
CompareEnumerableAnyOrderPankaj    : 00:00:03.2376179. Equal: True
CompareEnumerableAnyOrderGuru      : 00:00:01.0078811. Equal: True
CompareEnumerableAnyOrderEricO     : 00:00:01.5709527. Equal: True
End Compare

Start Compare for Int32 of size: 10000000
CompareEnumerableAnyOrderSergey    : 00:00:12.9824722. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:03.8619141. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:01.0829506. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:02.0760940. Equal: False
End Compare

Start Compare for String of size: 10000000
CompareEnumerableAnyOrderSergey    : 00:00:16.3923001. Equal: True
CompareEnumerableAnyOrderPankaj    : 00:00:54.0820172. Equal: True
CompareEnumerableAnyOrderGuru      : 00:00:53.5317528. Equal: True
CompareEnumerableAnyOrderEricO     : 00:00:05.9972781. Equal: True
End Compare

Start Compare for String of size: 10000000
CompareEnumerableAnyOrderSergey    : 00:00:14.3085444. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:54.4835349. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:53.6763668. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:05.6920709. Equal: False
End Compare

Start Compare for Int32 of size: 1000000
CompareEnumerableAnyOrderSergey    : 00:00:00.7707951. Equal: True
CompareEnumerableAnyOrderPankaj    : 00:00:00.2131278. Equal: True
CompareEnumerableAnyOrderGuru      : 00:00:00.0892336. Equal: True
CompareEnumerableAnyOrderEricO     : 00:00:00.5455456. Equal: True
End Compare

Start Compare for Int32 of size: 1000000
CompareEnumerableAnyOrderSergey    : 00:00:00.5982986. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:00.2248797. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:00.0937744. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:00.1462340. Equal: False
End Compare

Start Compare for String of size: 1000000
CompareEnumerableAnyOrderSergey    : 00:00:01.3971059. Equal: True
CompareEnumerableAnyOrderPankaj    : 00:00:04.3682767. Equal: True
CompareEnumerableAnyOrderGuru      : 00:00:03.7275648. Equal: True
CompareEnumerableAnyOrderEricO     : 00:00:00.4822032. Equal: True
End Compare

Start Compare for String of size: 1000000
CompareEnumerableAnyOrderSergey    : 00:00:01.1479243. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:04.2601652. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:03.7690159. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:00.2350588. Equal: False
End Compare

Start Compare for Int32 of size: 100000
CompareEnumerableAnyOrderSergey    : 00:00:00.1446754. Equal: True
CompareEnumerableAnyOrderPankaj    : 00:00:00.0212269. Equal: True
CompareEnumerableAnyOrderGuru      : 00:00:00.0087268. Equal: True
CompareEnumerableAnyOrderEricO     : 00:00:00.0081945. Equal: True
End Compare

Start Compare for Int32 of size: 100000
CompareEnumerableAnyOrderSergey    : 00:00:00.0245328. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:00.0232001. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:00.0075184. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:00.0139519. Equal: False
End Compare

Start Compare for String of size: 100000
CompareEnumerableAnyOrderSergey    : 00:00:00.0808056. Equal: True
CompareEnumerableAnyOrderPankaj    : 00:00:00.2401923. Equal: True
CompareEnumerableAnyOrderGuru      : 00:00:00.1936675. Equal: True
CompareEnumerableAnyOrderEricO     : 00:00:00.0392954. Equal: True
End Compare

Start Compare for String of size: 100000
CompareEnumerableAnyOrderSergey    : 00:00:00.1257959. Equal: False
CompareEnumerableAnyOrderPankaj    : 00:00:00.2398588. Equal: False
CompareEnumerableAnyOrderGuru      : 00:00:00.2811503. Equal: False
CompareEnumerableAnyOrderEricO     : 00:00:00.0406129. Equal: False
End Compare

测试代码:
using System.Diagnostics;
using System.Text;
using General.Compare;

namespace ConsoleAppTestEnumCompare
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // Show error in accepted solution of SO question
            int[] itemsToShowStackOverflowAcceptedAnswerError = new int[] { 2, 2, 3 };
            int[] itemsToShowStackOverflowAcceptedAnswerError2 = new int[] { 2, 3, 3 };
            Test(itemsToShowStackOverflowAcceptedAnswerError, itemsToShowStackOverflowAcceptedAnswerError2);

            // Major tests for int and string
            foreach (var countOfInt in new int[] { 100000000, 10000000, 1000000, 100000 })
            {
                Random rnd = new Random();
                List<int> list1 = new List<int>();
                for (int i = 0; i < countOfInt; i++)
                {
                    list1.Add(rnd.Next(list1.Count));
                }
                List<int> list2 = new List<int>(list1);
                list2.Sort();

                Test(list1, list2);

                list1[list1.Count / 2] = list1[list1.Count / 2] + 1;

                Test(list1, list2);

                List<string> listString1 = new List<string>();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < countOfInt; i++)
                {
                    sb.Clear();
                    int stringSize = rnd.Next(1, 12);
                    for (int n = 0; n < stringSize; n++)
                    {
                        sb.Append((char)rnd.Next((int)'a', (int)'z'));
                    }

                    listString1.Add(sb.ToString());
                }
                List<string> listString2 = new List<string>(listString1);
                listString2.Sort();

                Test(listString1, listString2);

                listString1[listString1.Count / 2] = listString1[listString1.Count / 2] + 1;

                Test(listString1, listString2);
            }
        }

        static void Test<T>(IEnumerable<T> list1, IEnumerable<T> list2)
        {
            (Func<IEnumerable<T>, IEnumerable<T>, bool> Compare, string Name)[] compareActions = new (Func<IEnumerable<T>, IEnumerable<T>, bool>, string)[5];
            compareActions[0] = (CompareEnumerableExtension.CompareEnumerableAnyOrderSelman, nameof(CompareEnumerableExtension.CompareEnumerableAnyOrderSelman));
            compareActions[1] = (CompareEnumerableExtension.CompareEnumerableAnyOrderSergey, nameof(CompareEnumerableExtension.CompareEnumerableAnyOrderSergey));
            compareActions[2] = (CompareEnumerableExtension.CompareEnumerableAnyOrderPankaj, nameof(CompareEnumerableExtension.CompareEnumerableAnyOrderPankaj));
            compareActions[3] = (CompareEnumerableExtension.CompareEnumerableAnyOrderGuru, nameof(CompareEnumerableExtension.CompareEnumerableAnyOrderGuru));
            compareActions[4] = (CompareEnumerableExtension.CompareEnumerableAnyOrderEricO, nameof(CompareEnumerableExtension.CompareEnumerableAnyOrderEricO));

            Debug.WriteLine($"Start Compare for {typeof(T).Name} of size: {list1.Count()}");
            Console.WriteLine($"Start Compare for {typeof(T).Name} of size: {list1.Count()}");

            bool areListsEqual;
            Stopwatch stopwatch;
             
            foreach(var compareAction in compareActions)
            {
                if (list1.Count() > 100 && compareAction.Name == nameof(CompareEnumerableExtension.CompareEnumerableAnyOrderSelman)) 
                {
                    continue; // Too slow and does not work
                }

                stopwatch = Stopwatch.StartNew();
                areListsEqual = compareAction.Compare(list1, list2);
                stopwatch.Stop();
                Debug.WriteLine($"{compareAction.Name, -33}  : {stopwatch.Elapsed}. Equal: {areListsEqual}");
                Console.WriteLine($"{compareAction.Name, -33}  : {stopwatch.Elapsed}. Equal: {areListsEqual}");
            }

            Debug.WriteLine("End Compare");
            Console.WriteLine("End Compare");

            Debug.WriteLine("");
            Console.WriteLine();
        }
    }
}

比较功能:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace General.Compare
{
    /// <summary>
    /// Based on question: https://dev59.com/7WEh5IYBdhLWcg3wpE3w
    /// </summary>
    public static class CompareEnumerableExtension
    {
        /// <summary>
        /// Check for Equality where items should be in the same order
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enum1"></param>
        /// <param name="enum2"></param>
        /// <returns>return true if both are equals</returns>
        public static bool CompareEnumerableInSameOrder<T>(this IEnumerable<T> enum1, IEnumerable<T> enum2)
        {
            return enum1.SequenceEqual(enum2);
        }

        /// <summary>
        /// Check for Equality in ANY order (SLOW).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enum1"></param>
        /// <param name="enum2"></param>
        /// <returns>return true if both are equals</returns>
        public static bool CompareEnumerableAnyOrderPankaj<T>(this IEnumerable<T> enum1, IEnumerable<T> enum2)
        {
            return Enumerable.SequenceEqual(enum1.OrderBy(fElement => fElement), enum2.OrderBy(sElement => sElement));
        }

        /// <summary>
        /// Check for Equality in ANY order (VERY SLOW). In reality, it just does not work with duplicate.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enum1"></param>
        /// <param name="enum2"></param>
        /// <returns></returns>
        public static bool CompareEnumerableAnyOrderSelman<T>(this IEnumerable<T> enum1, IEnumerable<T> enum2) 
        {
            return enum1.Count() == enum2.Count() && enum1.All(enum2.Contains);
        }

        /// <summary>
        /// Check for Equality in ANY order. Supposed to be Fast (according to Author in SO) but very slow.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enum1"></param>
        /// <param name="enum2"></param>
        /// <returns>return true if both are equals</returns>
        public static bool CompareEnumerableAnyOrderSergey<T>(this IEnumerable<T> enum1, IEnumerable<T> enum2)
        {
            var counts = enum1.GroupBy(v => v).ToDictionary(g => g.Key, g => g.Count());
            var ok = true;
            foreach (var n in enum2)
            {
                if (counts.TryGetValue(n, out int c))
                {
                    counts[n] = c - 1;
                }
                else
                {
                    ok = false;
                    break;
                }
            }
            return ok && counts.Values.All(c => c == 0);
        }

        /// <summary>
        /// Check for Equality in ANY order (ORDER DESTRUCTIVE on LIST). Just for fun.
        /// Code based on suggestion from "Guru Stron" on comments of my first answer
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enum1"></param>
        /// <param name="enum2"></param>
        /// <returns>return true if both are equals</returns>
        public static bool CompareEnumerableAnyOrderGuru<T>(this IEnumerable<T> enum1, IEnumerable<T> enum2)
        {
            var list1 = new List<T>(enum1);
            list1.Sort();

            var list2 = new List<T>(enum2);
            list2.Sort();

            return Enumerable.SequenceEqual(list1, list2);
        }

        /// <summary>
        /// Check for Equality in ANY order (Fast). Support null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enum1"></param>
        /// <param name="enum2"></param>
        /// <returns></returns>
        public static bool CompareEnumerableAnyOrderEricO<T>(this IEnumerable<T> enum1, IEnumerable<T> enum2)
        {
            if (enum1.Count() != enum2.Count())
                return false;

            // null value is never insert in the map, they are calculated outside of it.
#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
            Dictionary<T, int> counts = new Dictionary<T, int>(enum1.Count());
#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.

            int nullCount = 0;

            foreach (var n in enum1)
            {
                if (n == null)
                {
                    nullCount++;
                    continue;
                }

                if (counts.TryGetValue(n, out int c))
                {
                    counts[n] = c + 1;
                }
                else
                {
                    counts[n] = 1;
                }
            }

            var ok = true;
            foreach (var n in enum2)
            {
                if (n == null)
                {
                    nullCount++;
                    continue;
                }

                if (counts.TryGetValue(n, out int c))
                {
                    counts[n] = c - 1;
                }
                else
                {
                    ok = false;
                    break;
                }
            }
            return ok && nullCount == 0 && counts.Values.All(c => c == 0);
        }
    }
}

1
你在某些方面是对的,比如重复的数量或者数据类型(在问题中是'int',但实际上可能完全不同)。我认为计时器是可以的(不完美,但在这种情况下足够了)。我在我的测试中看到的更糟糕的问题是使用调试模式可能会导致相当偏颇的行为。我将尝试在发布版本中运行,并可能使用你的工具。但我已经花了很多时间只是为了将这个纯粹的信息发布给社区。我只是基于一些程序员的直觉,认为Sergey的代码并不快,并且有一些缓慢的地方。 - undefined
1
我建议尝试List<T>.Sort,除了OrderBy方法(当不需要保留原始顺序时,原地排序可以节省相当多的时间,与LINQ相比)。 - undefined
1
看看这个答案。虽然它不应该超过计数方法的复杂度(如果我没记错的话,是O(n)对比O(n log n) - 对我来说有点晚了 =) )。 - undefined
1
@GuruStron,你百分之百正确,我的测试是有偏见的,而且现在还是有偏见。但我添加了字符串只是为了提供更好的概述。另一个原因是你在"int"上比我快;-) !!! 谢谢你的建议。我太懒了,不想用BEnchmarkDotNet,但非常感谢你让我知道。知道有这个工具真的让我很开心。 - undefined
1
只是一个小问题 - “几乎已经有10年的时间了,被接受的答案根本不起作用。” - 它仍然有效(或者从某种观点来看,它从来没有起作用) - 显然,这个答案没有考虑到重复的情况,而且这可能是一个有效的情况(尤其是原始问题中除了id5之外没有重复的情况,这可能只是一个拼写错误)。 - undefined
显示剩余9条评论

-1
如果您想在相同的序列中获得相等的值或对象,则这些扩展可能对您有用:(相等意味着值相等)

对于包含值类型的列表:

public static bool ContainsEqualValuesInSameSequence<T>(this IReadOnlyList<T> left, IReadOnlyList<T> right)
    // restricted to value types since we do not do any null checking on the values.
    where T : struct, IEquatable<T>
{
    if (ReferenceEquals(left, right))
    {
        return true;
    }

    if (left == null || right == null)
    {
        return false;
    }

    if (left.Count != right.Count)
    {
        return false;
    }

    for (var i = 0; i < left.Count; i += 1)
    {
        var leftItem = left[i];
        var rightItem = right[i];

        if (leftItem.Equals(rightItem) == false)
        {
            return false;
        }
    }

    return true;
}

对于包含引用类型的列表:

public static bool ContainsEqualObjectsInSameSequence<T>(this IReadOnlyList<T> left, IReadOnlyList<T> right)
    // restricted to reference types since we do null checking on the objects.
    where T : class, IEquatable<T>
{
    if (ReferenceEquals(left, right))
    {
        return true;
    }

    if (left == null || right == null)
    {
        return false;
    }

    if (left.Count != right.Count)
    {
        return false;
    }

    for (var i = 0; i < left.Count; i += 1)
    {
        var leftItem = left[i];
        var rightItem = right[i];

        if (leftItem == null)
        {
            if (rightItem != null)
            {
                return false;
            }
        }
        else if (leftItem.Equals(rightItem) == false)
        {
            return false;
        }
    }

    return true;
}

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