比较类似于Diff Tool的两个类对象列表

时间:2017-07-17 23:23:07

标签: c# linq diff iequalitycomparer compareobject

提问here

我的要求是编写一个模仿diff工具的程序。是的,有很多库和开源代码可以实现这个目的,但我想编写自己的比较器。

这是起点。我有一个名为DataItem的类,如下所示:

public class DataItem
{
    public DataItem() { }
    public DataItem(string d, string v) { Data = d; Value = v; }

    public string Data { get; set; }
    public string Value { get; set; }
}

我有两个这些类对象的列表,让我们将它们称为PRE和POST,并按如下方式获取一些示例值。 '数据'部分在列表中将是唯一的。

preList: (Data,Value)
AAA,0
BBB,1
CCC,3
DDD,4
FFF,0
GGG,3

postList: (Data,Value)
AAA,0
BBB,2
DDD,4
EEE,9
FFF,3

将PRE视为原始列表,并在完成某些更改后将POST视为列表。我想比较两者,并将它们分为三类:

  1. 已添加项目 - 包含新数据'的项目。添加到列表中。
  2. 已移除的项目 - 已从列表中删除项目。
  3. 差异项目 - '数据'在PRE和POST列表中都可以找到它们,但是它们对应的值是'是不同的。
  4. 因此,在分类时,它们应如下所示:

    Added Items:
    EEE,9
    
    Removed Items:
    CCC,3
    GGG,3
    
    Diff Items:
    BBB
    FFF
    

    我有另一个DiffItem类,我希望将最终结果放在哪些对象上。 DiffItem看起来像这样:

    public class DiffItem
    {
        public DiffItem() { }
        public DiffItem(string data, string type, string pre, string post) { Data = data; DiffType = type; PreVal = pre; PostVal = post; }
    
        public string Data { get; set; }
        public string DiffType { get; set; } // DiffType = Add/Remove/Diff
        public string PreVal { get; set; } // preList value corresponding to Data item
        public string PostVal { get; set; } // postList value corresponding to Data item
    }
    

    为了实现这一点,首先我扩展了IEqualityComparer并编写了几个比较器:

    public class DataItemComparer : IEqualityComparer<DataItem>
    {
        public bool Equals(DataItem x, DataItem y)
        {
            return (string.Equals(x.Data, y.Data) && string.Equals(x.Value, y.Value));
        }
    
        public int GetHashCode(DataItem obj)
        {
            return obj.Data.GetHashCode();
        }
    }
    
    public class DataItemDataComparer : IEqualityComparer<DataItem>
    {
        public bool Equals(DataItem x, DataItem y)
        {
            return string.Equals(x.Data, y.Data);
        }
    
        public int GetHashCode(DataItem obj)
        {
            return obj.Data.GetHashCode();
        }
    }
    

    然后使用Except()和Intersect()方法,如下所示:

        static void DoDiff()
        {
            diffList = new List<DiffItem>();
    
            IEnumerable<DataItem> preOnly = preList.Except(postList, new DataItemComparer());
            IEnumerable<DataItem> postOnly = postList.Except(preList, new DataItemComparer());
            IEnumerable<DataItem> common = postList.Intersect(preList, new DataItemComparer());
    
            IEnumerable<DataItem> added = postOnly.Except(preOnly, new DataItemDataComparer());
            IEnumerable<DataItem> removed = preOnly.Except(postOnly, new DataItemDataComparer());
            IEnumerable<DataItem> diffPre = preOnly.Intersect(postOnly, new DataItemDataComparer());
            IEnumerable<DataItem> diffPost = postOnly.Intersect(preOnly, new DataItemDataComparer());
    
            foreach (DataItem add in added)
            {
                diffList.Add(new DiffItem(add.Data, "Add", null, add.Value));
            }
            foreach (DataItem rem in removed)
            {
                diffList.Add(new DiffItem(rem.Data, "Remove", rem.Value, null));
            }
            foreach (DataItem pre in diffPre)
            {
                DataItem post = diffPost.First(x => x.Data == pre.Data);
                diffList.Add(new DiffItem(pre.Data, "Diff", pre.Value, post.Value));
            }
        }
    

    这确实有效,可以完成工作。但是我想知道是否有更好的&#39;这样做的方式。请注意,我在“更好”这个词后加上引号,因为我没有正确定义什么会让这更好。也许有一种方法可以在没有尽可能多的“预告”的情况下完成这项工作。循环和使用Except()和Intersetc(),因为我可以想象Linq背后有相当多的迭代。

    简单地说,我可以为此编写一个更清晰的代码吗?我主要是出于学术兴趣并扩展我的知识。

1 个答案:

答案 0 :(得分:0)

我认为您不需要IEqualityComparer:

var added = from a in postList
            where !preList.Any(b => b.Data == a.Data)
            select new DiffItem(a.Data, "Add", null, a.Value);
var removed = from b in preList
              where !postList.Any(a => a.Data == b.Data)
              select new DiffItem(b.Data, "Remove", b.Value, null);
var diff = from b in preList
           join a in postList on b.Data equals a.Data
           where b.Value != a.Value
           select new DiffItem(b.Data, "Diff", b.Data, a.Data);
var diffList = added.ToList();
diffList.AddRange(removed);
diffList.AddRange(diff);