【问题标题】:Compare two collection values c#比较两个集合值c#
【发布时间】:2017-07-14 09:08:11
【问题描述】:

我有两个具有相同值的集合,但它们具有不同的引用。在没有 foreach 语句的情况下比较两个集合的最佳方法是什么, 下面是我创建的示例应用程序,

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace CollectionComparer
{
  public class Program
  {
    private static void Main(string[] args)
    {
        var persons = GetPersons();
        var p1 = new ObservableCollection<Person>(persons);
        IList<Person> p2 = p1.ToList().ConvertAll(x =>
            new Person
            {
                Id = x.Id,
                Age = x.Age,
                Name = x.Name,
                Country = x.Country
            });

        //p1[0].Name = "Name6";
        //p1[1].Age = 36;

        if (Equals(p1, p2))
            Console.WriteLine("Collection and its values are Equal");
        else
            Console.WriteLine("Collection and its values are not Equal");
        Console.ReadLine();
    }

    public static IEnumerable<Person> GetPersons()
    {
        var persons = new List<Person>();
        for (var i = 0; i < 5; i++)
        {
            var p = new Person
            {
                Id = i,
                Age = 20 + i,
                Name = "Name" + i,
                Country = "Country" + i
            };
            persons.Add(p);
        }
        return persons;
    }
  }
}

public class Person
{
  public int Id { get; set; }
  public string Name { get; set; }
  public int Age { get; set; }
  public string Country { get; set; }
}

在上面的代码中,我需要比较集合 p1 和 p2。但结果总是出现“集合及其值不相等”,因为两个集合具有不同的参考。 有没有一种通用的方法来进行这种比较而不使用 foreach 并比较类型特定的属性。

【问题讨论】:

  • 你可以为你的Person 类覆盖Equals
  • 为什么不想使用 foreach 循环?那么 for 循环呢?
  • 顺序重要还是要检查两个集合中的人物是否相同?
  • 比如你有两个List&lt;Person&gt;p1p2var areEqual = p1.All(p =&gt; p2.Contains(p);
  • 这里的顺序并不重要。我只想检查一个项目是否在一个集合中添加、删除或编辑。

标签: c# .net generics collections reference


【解决方案1】:

您可以使用Enumerable.SequenceEqual&lt;T&gt;(IEnumerable&lt;T&gt; first, IEnumerable&lt;T&gt; second)

这将按顺序比较两个序列,如果其中包含的项目相等且都具有相同数量的元素,则返回true

一个警告,因为Person 不会覆盖Equals(object obj)SequenceEqual 将在比较任意两个 Person 对象时执行默认的引用相等检查,您可能需要值相等语义.要解决这个问题,至少要覆盖 bool Equals(object obj)int GetHashCode()(同时实现 IEquatable&lt;Person&gt; 是一种很好的做法):

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Country { get; set; }

    public override bool Equals(object obj)
    {
         var person = obj as Person;

         if (person == null) return false;

         return person.Id == Id && person.Name = Name && //etc.
    }

    public override int GetHashCode() 
        => Id.GetHashCode() ^ Name.GetHashCode() ^ //etc.
}

如果您无法修改 Person,那么您可以定义自己的 IEqualityComparer&lt;Person&gt; 并将其交给 SequenceEquals,以便它可以执行除默认引用相等之外的相等检查。

更新:如果顺序不重要,那么您可以使用UnionExcept。这可能会很快变慢,因此您至少应该考虑将您正在比较的集合转换为某种类型的 Set 之前的可能性。

UPDATE2Enumerable.SequenceEqual 实际上是一个扩展 方法,应该这样调用:p1.SequenceEquals(p2)

【讨论】:

  • 我觉得上面的代码可以用,但是上面的场景两个采集顺序应该是一样的。但在我的收藏中,顺序可能会有所不同。
  • 感谢您的帮助,当我有值类型时,此“除外”工作正常,但对引用类型属性有效。如果我将 Id 更改为 Guid,则在同一 Person 类中的示例。这个比较有效。有什么办法可以解决这个问题。
【解决方案2】:

您可以使用 ICompareable 接口并实现自己的比较功能。可以参考以下链接https://msdn.microsoft.com/de-de/library/system.icomparable(v=vs.110).aspx

【讨论】:

    猜你喜欢
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多