简体   繁体   English

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

[英]Enumerable.Except does not use my custom comparer

I try to use the except method with a custom equality comparer, but it is not work. 我尝试使用自定义相等比较器的except方法,但它不起作用。

My equality comparer: 我的平等比较器:

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
}

My except usage: 我的除了用法:

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

The strange thing is, event I provide my custom equality comparer the default one is used, so what do I make wrong? 奇怪的是,事件我提供我的自定义相等比较器使用默认值,所以我做错了什么?

Thanks for help. 感谢帮助。

Similar to Marc I just tested this, everything is being called just fine, my guess is that you are caught by the LINQ deferred execution, notice the ToArray in my code. 类似于Marc,我刚测试了这个,一切都被称为正常,我的猜测是你被LINQ延迟执行捕获,注意我的代码中的ToArray。

Note, when tracing through this I noticed GetHashCode is never called on null objects in the comparer. 请注意,在跟踪此操作时,我注意到比较器中的空对象永远不会调用GetHashCode。

Keep in mind, MiscUtil has an awesome way for you to do this stuff inline, see: Can I specify my explicit type comparator inline? 请记住,MiscUtil有一个很棒的方式让你内联这些东西,请参阅: 我可以指定我的显式类型比较器内联吗?

Or you could adapt this to Except: Distinct list of objects based on an arbitrary key in LINQ 或者你可以将它改编为Except: 基于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();
        }
    }
}

Try: 尝试:

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

Your hash-code must match equality (or at least, not contradict it); 您的哈希码必须匹配相等(或者至少不与它相矛盾); and your equality says "nulls are equal, otherwise compare the guid". 你的平等说“空位是平等的,否则比较指南”。 Internally, I expect Except uses a HashSet<T> , which explains why getting GetHashCode right is so important . 在内部,我希望Except使用HashSet<T> ,这解释了为什么让GetHashCode正确是如此重要


Here's my test rig (using the above GetHashCode ) which works fine: 这是我的测试装备(使用上面的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
        }
    }
}

If your version doesn't work, you're going to have to post something about how you're using it, as it works fine for me (above). 如果您的版本不起作用,您将不得不发布一些关于您如何使用它的内容,因为它对我来说很好(上图)。

The methods in your equality comparer doesn't match up. 您的相等比较器中的方法不匹配。 You are comparing the GUID of the objects, but the GetHashCode method uses the default implementation which is based on the reference, not the GUID. 您正在比较对象的GUID,但GetHashCode方法使用基于引用的默认实现,而不是GUID。 As different instances will get different hash codes eventhough they have the same GUID, the Equals method will never be used. 由于不同的实例将获得不同的哈希码,尽管它们具有相同的GUID,但永远不会使用Equals方法。

Get the hash code for the GUID in the GetHashCode method, as that is what you are comparing: GetHashCode方法中获取GUID的哈希码,因为这是您要比较的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM