Я пытаюсь использовать метод 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);
Странная вещь: событие, которое я предоставляю своему пользовательскому сопоставлению равенства, используется по умолчанию, поэтому что я делаю неправильно?
Спасибо за помощь.
Подобно Marc, я просто тестировал это, все называется просто отлично, я предполагаю, что вы попали в отложенное выполнение LINQ, обратите внимание на ToArray в моем коде.
Обратите внимание, что при прослеживании этого я заметил, что GetHashCode никогда не вызывается для нулевых объектов в компараторе.
Имейте в виду, что у MiscUtil есть замечательный способ сделать это, встроенный в строку: Могу ли я указать мой явный тип компаратора inline?
Или вы можете адаптировать это значение к 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();
}
Ваш хэш-код должен соответствовать равенству (или, по крайней мере, не противоречить ему); и ваше равенство говорит, что "нули равны, в противном случае сравнивают guid". Внутри я ожидаю, что Except
использует HashSet<T>
, что объясняет, почему получение GetHashCode
right настолько важно.
Здесь моя тестовая установка (с использованием выше 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
}
}
}
Если ваша версия не работает, вам придется публиковать что-то о том, как вы ее используете, поскольку она отлично работает для меня (см. выше).
Методы в вашем сопоставлении равенства не совпадают. Вы сравниваете GUID объектов, но метод GetHashCode
использует реализацию по умолчанию, основанную на ссылке, а не на GUID. Поскольку разные экземпляры получат разные хэш-коды, хотя они имеют один и тот же идентификатор GUID, метод Equals
никогда не будет использоваться.
Получить хэш-код для GUID в методе GetHashCode
, поскольку это то, что вы сравниваете:
public int GetHashCode(T obj) {
if (obj == null) {
throw new ArgumentNullException("obj");
}
return obj.Guid.GetHashCode();
}