Странное поведение на IEqualityComparer
Я пытаюсь реализовать IEqualityComparer для моего объекта, который в основном определяет, является ли объект старше другого. Следующий простой пример синтезирует то, что я пытаюсь выполнить:
class Program
{
static void Main(string[] args)
{
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
};
var authorsListExcept = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
};
var authorsList2 = authorsList
.Except(authorsListExcept, new AuthorUpdatedComparer()).ToList();
}
}
class Author
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}
class AuthorUpdatedComparer : IEqualityComparer<Author>
{
public bool Equals(Author x, Author y)
{
return x.Age >= y.Age;
}
public int GetHashCode(Author obj)
{
//Check whether the object is null
if (Object.ReferenceEquals(obj, null)) return 0;
int FirstnameHash = (obj.Firstname ?? "").GetHashCode();
int LastnameHash = (obj.Lastname ?? "").GetHashCode();
int finalResult = FirstnameHash ^ LastnameHash;
return finalResult;
}
}
Мой результат для авторов списка List2: * Боб Смит с 20 лет * Бом Смит с 14 лет * Тревор Смит с 15 лет * Брайан Смит с 11 лет * Билли Смит с 11 лет
Но вместо этого Боб Смит с 14 лет не входит. При отладке я пришел к выводу, что Comparer после включения Боба Смита с 20-летнего возраста начала использовать его, если не считать того, что Боб младше 20 лет.
Это странное поведение, с моей точки зрения, оно должно исключать только тех, кто моложе или того же возраста, что и авторы ListExcept. Я попытался прочитать документацию msdn, и то, что я хочу, должно произойти: font: http://msdn.microsoft.com/en-us/library/bb336390(v=vs.100).aspx
Кто-нибудь может мне помочь? Спасибо, Хьюго Сальгадо
2 ответа
Следующий запрос LINQ предоставляет ожидаемый результат:
var result =
authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
.SelectMany(g => g.Where(x => authorsListExcept.All(e => e.Firstname != x.Firstname || e.Lastname != x.Lastname || e.Age < x.Age)));
Следующий запрос также дает этот результат. Это должно работать лучше:
var result =
authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
.GroupJoin(authorsListExcept, x => x.Key,
x => Tuple.Create(x.Firstname, x.Lastname),
(a, e) => a.Where(x => x.Age > e.Select(z => z.Age)
.DefaultIfEmpty(0)
.Max()))
.SelectMany(x => x)
И третий вариант (такой же, как предыдущий, но с синтаксисом запроса):
var result =
(from a in authorsList
group a by Tuple.Create(a.Firstname, a.Lastname) into g
join e in authorsListExcept on g.Key equals Tuple.Create(e.Firstname, e.Lastname) into er
from age in er.Select(x => x.Age).DefaultIfEmpty()
select g.Where(x => x.Age > age)).SelectMany(x => x);
Интерфейс IEqualityComparer<T>
там, чтобы проверить на равенство. Это не имеет никакого отношения к любому заказу. Таким образом, вы не можете использовать его так, как вы пытаетесь.
В целом: реализация этого интерфейса всегда должна использовать один и тот же набор свойств в обоих GetHashCode
реализация и Equals
метод.
Если я понял, что вы хотите, попробуйте это.
class Program
{
static void Main(string[] args)
{
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
};
var authorsListExcept = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
};
var authorsList2 = authorsList.Where(x => !authorsListExcept.Any(y => y.Firstname == x.Firstname && y.Lastname == x.Lastname && x.Age <= y.Age));
}
}
public class Author
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}