Enumerable.Except不使用我的自定义比较器

时间:2009-08-24 08:17:32

标签: c# linq extension-methods

我尝试将except方法与自定义相等比较器一起使用,但它不起作用。

我的平等比较器:

public class BusinessObjectGuidEqualityComparer<T> : IEqualityComparer<T> where T : BusinessObject
{
    #region IEqualityComparer<T> Members

    /// <summary>
    /// Determines whether the specified objects are equal.
    /// </summary>
    /// <param name="x">The first object of type <paramref name="T"/> to compare.</param>
    /// <param name="y">The second object of type <paramref name="T"/> to compare.</param>
    /// <returns>
    /// <see langword="true"/> If the specified objects are equal; otherwise, <see langword="false"/>.
    /// </returns>
    public bool Equals(T x, T y)
    {
        return (x == null && y == null) || (x != null && y != null && x.Guid.Equals(y.Guid)); 
    }

    /// <summary>
    /// Returns a hash code for this instance.
    /// </summary>
    /// <param name="obj">The object to get the hash code.</param>
    /// <returns>
    /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
    /// </returns>
    /// <exception cref="T:System.ArgumentNullException">
    /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
    /// </exception>
    public int GetHashCode(T obj)
    {
        if (obj == null)
        {
            throw new ArgumentNullException("obj");
        }

        return obj.GetHashCode();
    }

    #endregion
}

我的用法除外:

BusinessObjectGuidEqualityComparer<Area> comparer = new BusinessObjectGuidEqualityComparer<Area>();
IEnumerable<Area> toRemove = this.Areas.Except(allocatedAreas, comparer);
IEnumerable<Area> toAdd = allocatedAreas.Except(this.Areas, comparer);

奇怪的是,事件我提供了我的自定义相等比较器,使用了默认值,所以我做错了什么?

感谢您的帮助。

3 个答案:

答案 0 :(得分:4)

与Marc类似,我刚刚测试了这个,一切都被称为正常,我的猜测是你被LINQ延迟执行捕获,注意我的代码中的ToArray。

注意,在跟踪这个时,我注意到从不在比较器中的null对象上调用GetHashCode。

请记住,MiscUtil有一种很棒的方式让你内联这些东西,请参阅:Can I specify my explicit type comparator inline?

或者你可以将其改编为:Distinct list of objects based on an arbitrary key in LINQ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

    public class BusinessObject {
        public Guid Guid { get; set; }
    }

    public class BusinessObjectGuidEqualityComparer<T> : IEqualityComparer<T> where T : BusinessObject {
        #region IEqualityComparer<T> Members

        public bool Equals(T x, T y) {
            return (x == null && y == null) || (x != null && y != null && x.Guid.Equals(y.Guid));
        }

        /// </exception>
        public int GetHashCode(T obj) {
            if (obj == null) {
                throw new ArgumentNullException("obj");
            }

            return obj.GetHashCode();
        }

        #endregion
    }

    class Program {
        static void Main(string[] args) {

            var comparer = new BusinessObjectGuidEqualityComparer<BusinessObject>();

            List<BusinessObject> list1 = new List<BusinessObject>() {
                new BusinessObject() {Guid = Guid.NewGuid()},
                new BusinessObject() {Guid = Guid.NewGuid()}
            };

            List<BusinessObject> list2 = new List<BusinessObject>() {
                new BusinessObject() {Guid = Guid.NewGuid()},
                new BusinessObject() {Guid = Guid.NewGuid()},
                null,
                null,
                list1[0]
            };

            var toRemove = list1.Except(list2, comparer).ToArray();
            var toAdd = list2.Except(list1, comparer).ToArray();

            // toRemove.Length == 1
            // toAdd.Length == 2
            Console.ReadKey();
        }
    }
}

答案 1 :(得分:3)

尝试:

public int GetHashCode(T obj) {
    return obj == null ? 0 : obj.Guid.GetHashCode();
}

你的哈希码必须匹配相等(或者至少不与它相矛盾);你的平等说“空位是平等的,否则比较指南”。在内部,我希望Except使用HashSet<T>,这解释了为什么GetHashCode正确is so important


这是我的测试装备(使用上面的GetHashCode),工作正常:

public abstract class BusinessObject {
    public Guid Guid { get; set; }
}
class Area : BusinessObject {
    public string Name { get; set; }
    static void Main() {
        Guid guid = Guid.NewGuid();
        List<Area> areas = new List<Area> {
            new Area { Name = "a", Guid = Guid.NewGuid() },
            new Area { Name = "b", Guid = guid },
            new Area { Name = "c", Guid = Guid.NewGuid() },
        };
        List<Area> allocatedAreas = new List<Area> {
            new Area { Name = "b", Guid = guid},
            new Area { Name = "d", Guid = Guid.NewGuid()},
        };
        BusinessObjectGuidEqualityComparer<Area> comparer =
             new BusinessObjectGuidEqualityComparer<Area>();
        IEnumerable<Area> toRemove = areas.Except(allocatedAreas, comparer);
        foreach (var row in toRemove) {
            Console.WriteLine(row.Name); // shows a & c, since b is allocated
        }
    }
}

如果您的版本不起作用,您将不得不发布一些关于您如何使用它的内容,因为它适用于我(上图)。

答案 2 :(得分:1)

您的相等比较器中的方法不匹配。您正在比较对象的GUID,但GetHashCode方法使用基于引用的默认实现,而不是GUID。由于不同的实例将获得不同的哈希码,尽管它们具有相同的GUID,但永远不会使用Equals方法。

GetHashCode方法中获取GUID的哈希码,因为这是您要比较的内容:

public int GetHashCode(T obj) {
    if (obj == null) {
        throw new ArgumentNullException("obj");
    }
    return obj.Guid.GetHashCode();
}