代码之家  ›  专栏  ›  技术社区  ›  Enyra

可枚举。但不使用自定义比较器

  •  2
  • Enyra  · 技术社区  · 16 年前

    我尝试将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 回复  |  直到 16 年前
        1
  •  4
  •   Community Mohan Dere    8 年前

    与我刚测试过的Marc类似,所有的东西都被称为“很好”,我猜你被Linq延迟执行捕获了,注意我代码中的ToArray。

    注意,在跟踪这个过程时,我注意到在比较器中从未对空对象调用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();
            }
        }
    }
    
        2
  •  3
  •   Community Mohan Dere    8 年前

    尝试:

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

    您的散列码必须匹配相等(或者至少不与之冲突);并且您的相等表示“空值相等,否则比较guid”。我想在内部 Except 使用A HashSet<T> 这就解释了为什么 GetHashCode 正确的 is so important .


    这是我的测试装置(使用上面的 方法 )很好用:

    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
            }
        }
    }
    

    如果你的版本不起作用,你将不得不发布一些关于你如何使用它的东西,因为它对我很好(上面)。

        3
  •  1
  •   Guffa    16 年前

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

    获取guid的哈希代码 方法 方法,正如您所比较的:

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