自定义引用类型的Enumerable.Union调用(原创)

在开发的过程中常常会碰到一种需求,两个集合融合到一个集合里面,同时去除相同的成员。

说到这里,可能很多人脑中立刻就闪现了Union,Distinct之类的函数。的确,这两个函数可以解决大部分的问题。根据处理的对象的类型不同,实现需求的代码和原理都不一样。

1. 对象是系统自带的值类型。可以直接调用Union,即可实现上面的需求。

var x = new List<int> { 1, 2, 3, 4, 5, 6 };
var y = new List<int> { 6, 7, 8, 9, 10 };
var res = x.Union(y).ToList();//结果为{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

2. 对象是系统自带的引用类型。由于对象为引用类型,在没有特殊处理的前提下,实际执行Union操作时,进行相同比较时的参照为对象的引用地址,表面上看起来是一样的,但是后台的引用地址不同,比较是否相同时就会返回‘不一样’。这种情况下,需要自定义一个实现了IEqualityComparer<T>的类作为参数传递进去

var x = new List<List<int>> { new List<int>() {1}, new List<int>() {2}, new List<int>() {3} };
var y = new List<List<int>> { new List<int>() {3}, new List<int>() {4}, new List<int>() {5} };
var res = x.Union(y).ToList();//结果Count为6

查看Union的函数,它有一个重载,Union(IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)。也就是说我们可以自定义相同比较的规则。修改代码如下:

 public Form2()
 {
     InitializeComponent();
     var x = new List<List<int>> {new List<int>() {1}, new List<int>() {2}, new List<int>() {3}};
     var y = new List<List<int>> {new List<int>() {3}, new List<int>() {4}, new List<int>() {5}};
     var res = x.Union(y, new ListComparer<int>()).ToList();//结果Count为5
 }
 class ListComparer<T> : IEqualityComparer<List<T>>
 {
     public bool Equals(List<T> x, List<T> y)
     {
         var except = x.Except(y);
         return except.Count() == 0 && x.Count == y.Count;
     }

     public int GetHashCode(List<T> obj)
     {
         return obj.Select(x => x.GetHashCode()).Aggregate((x, y) => x ^ y);
     }
 }

3. 对象是自定义的值类型自定义的值类型需要在定义过程中按照自己的需要重写Equals的方法。如果不重写也可以,在执行Union函数时,会调用系统的ValueType.Equals方法。只是这样就不一定是我们想要的结果了。贴上代码一目了然。在下面的TestB中,我要求只要ID相同,实例就是相同的。根据这个规则,最后的res.Count为4.

public Form2()
{
    InitializeComponent();
    var x = new List<TestB> {new TestB("One", "1"), new TestB("Two", "2"), new TestB("Three", "3")};
    var y = new List<TestB> {new TestB("One", "4"), new TestB("Two", "5"), new TestB("Six", "6")};
    var res = x.Union(y).ToList();
}

struct TestB
{
    public string ID;
    public string Value;
    public TestB(string id, string value)
    {
        this.ID = id;
        this.Value = value;
    }
    public override string ToString()
    {
        return string.Format("{0}_{1}", ID, Value);
    }

    public override bool Equals(object obj)
    {
        if (obj is TestB)
            return this.ID.Equals(((TestB)obj).ID);
        else return false;
    }
}

4. 对象是自定义的引用类型。综合以上三种情况的分析和实现方法,不难得出这种情况下有两种实现方法。

方法一:直接在自定义的过程中,重写GetHashCode和Equals函数;

public Form2()
{
    InitializeComponent();
    var x = new List<TestA> {new TestA("One", "1"), new TestA("Two", "2"), new TestA("Three", "3")};
    var y = new List<TestA> {new TestA("One", "1"), new TestA("Two", "5"), new TestA("Six", "6")};
    var res = x.Union(y).ToList();
}

class TestA 
{
    public string ID;
    public string Value;
    public TestA(string id, string value)
    {
        this.ID = id;
        this.Value = value;
    }
    public override string ToString()
    {
        return string.Format("{0}_{1}", ID, Value);
    }

    public override bool Equals(object obj)
    {
        if (obj is TestA)
            return this.GetHashCode().Equals(((TestA)obj).GetHashCode());
        else return false;
    }

    public override int GetHashCode()
    {
        int hashID = ID.GetHashCode();
        int hashValue = Value.GetHashCode();
        return hashID ^ hashValue;
    }
}

方法二:另外定义一个类,实现IEqualityComparer<T>,作为相同的判断规则传入到Union函数中。

public Form2()
{
    InitializeComponent();
    var x = new List<TestA> { new TestA("One", "1"), new TestA("Two", "2"), new TestA("Three", "3") };
    var y = new List<TestA> { new TestA("One", "1"), new TestA("Two", "5"), new TestA("Six", "6") };
    var res = x.Union(y, new TestAComparer()).ToList();
}

class TestA
{
    public string ID;
    public string Value;
    public TestA(string id, string value)
    {
        this.ID = id;
        this.Value = value;
    }
    public override string ToString()
    {
        return string.Format("{0}_{1}", ID, Value);
    }
}
class TestAComparer : IEqualityComparer<TestA>
{
    public bool Equals(TestA x, TestA y)
    {
        return GetHashCode(x).Equals(GetHashCode(y));
    }

    public int GetHashCode(TestA obj)
    {
        int hashID = obj.ID.GetHashCode();
        int hashValue = obj.Value.GetHashCode();
        return hashID ^ hashValue;
    }
}

留文备用。转载请注明出处:http://www.cnblogs.com/icyJ/

posted @ 2012-11-10 10:55  脸谱匠  阅读(2029)  评论(1编辑  收藏  举报