Общий метод, чтобы получить отличный от LIST‹T›

Я пытаюсь сравнить (значения свойств) экземпляр типа в списке и устранить дубликаты. Согласно MSDN GetHashCode() — один из способов сравнения двух объектов.

Хэш-код предназначен для эффективной вставки и поиска в коллекциях, основанных на хэш-таблице. Хэш-код не является постоянным значением

Учитывая это, я начал писать свой метод расширения, как показано ниже.

 public static class Linq 
    {
  public static IEnumerable<T> DistinctObjects<T>(this IEnumerable<T> source)
        {
            List<T> newList = new List<T>();
            foreach (var item in source)
            {
                if(newList.All(x => x.GetHashCode() != item.GetHashCode()))
                    newList.Add(item);
            }
            return newList;
        }
}

Это условие всегда дает мне false, хотя данные объекта одинаковы.

newList.All(x => x.GetHashCode() != item.GetHashCode())

Наконец, я хотел бы использовать его как

MyDuplicateList.DistinctObjects().ToList();

Если сравнение всех полей объекта слишком сложно, я могу использовать его, например,

 MyDuplicateList.DistinctObjects(x=>x.Id, x.Name).ToList();

Здесь я говорю сравнить только эти два поля этих объектов.


person HaBo    schedule 19.10.2016    source источник
comment
Существует встроенная функция LINQ, Distinct(), которая принимает выражение для определения уникальности. Разве это не делает то, что вы хотите?   -  person GEEF    schedule 19.10.2016
comment
Прежде всего - если GetHashCode равен, это не означает, что объекты равны, это наоборот   -  person MistyK    schedule 19.10.2016
comment
@GEEF .Distinct() не выполняет свою работу.   -  person HaBo    schedule 19.10.2016
comment
@YacoubMassad GetHashCode() определенно реализован любым T, учитывая, что он основан на C# object.   -  person GEEF    schedule 19.10.2016
comment
Это не выполняет работу, потому что вам нужно использовать компаратор или реализовать интерфейс IEquatable‹T› для объекта, переопределить equals и gethashcode, иначе он сравнивает ссылки   -  person MistyK    schedule 19.10.2016
comment
@HaBo см. это: stackoverflow.com/questions/10719928/   -  person GEEF    schedule 19.10.2016
comment
@GEEF, это решение для ссылок предлагает делать анонимный выбор, чего я не хочу, я хочу, чтобы эта функция была как можно более общей, чтобы я мог использовать ее во всех возможных коллекциях дубликатов.   -  person HaBo    schedule 19.10.2016
comment
@Zbigniew Zbigniew, вы говорите, что если у меня есть класс Customer, мне нужно реализовать на нем IEquatable‹Customer›, тогда, если я сделаю MyDuplicateCustomerList.Distinct(), то это даст мне уникальные результаты? Я прав?   -  person HaBo    schedule 19.10.2016
comment
@HaBO нет, если вы также не переопределите функции Equals и Hashcode. Вот ссылка: blogs.msdn.microsoft.com/jaredpar/2009/01/15/   -  person MistyK    schedule 19.10.2016
comment
@Zbigniew Технически это сработает, даже если вы не переопределите Equals, это просто ужасная идея не переопределять также Equals, так как это будет супер сбивать с толку пользователей типа, если два метода Equals включены объект функционирует по-разному.   -  person Servy    schedule 19.10.2016
comment
@Servy это будет работать в этом примере, но, как указано в ссылке, которую я предоставил, это не будет работать для неуниверсальных коллекций. И вторая причина - это причина, которую вы указали   -  person MistyK    schedule 19.10.2016
comment
@Zbigniew Я не говорю, что он не должен этого делать, я просто говорю, что, когда вы сказали, что ему нужно это сделать ради данного примера, это не так, это просто хорошая идея, поскольку она заставит другие ситуации функционировать должным образом.   -  person Servy    schedule 19.10.2016
comment
@Servy да, ты прав. Я только что прочитал это снова   -  person MistyK    schedule 19.10.2016


Ответы (2)


Прочитав ваши комментарии, я бы предложил следующее решение:

   public static IEnumerable<TSource> DistinctBy<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        HashSet<TResult> set = new HashSet<TResult>();

        foreach(var item in source)
        {
            var selectedValue = selector(item);

            if (set.Add(selectedValue))
                yield return item;
        }
    }

Затем вы можете использовать его следующим образом:

var distinctedList = myList.DistinctBy(x => x.A);

или для нескольких таких свойств:

var distinctedList = myList.DistinctBy(x => new {x.A,x.B});

Преимущество этого решения в том, что вы можете точно указать, какие свойства следует использовать для различия, и вам не нужно переопределять Equals и GetHashCode для каждого объекта. Вы должны убедиться, что ваши свойства можно сравнивать.

person MistyK    schedule 19.10.2016
comment
Подождите секунду, это возвращает только объект, который я передаю в Distinct(x=›x.Id)? - person HaBo; 19.10.2016
comment
@HaBo нет, тип возвращаемого значения — IEnumerable‹TSource›. Пожалуйста, прочитайте об этом в MSDN. - person MistyK; 19.10.2016
comment
хороший способ сделать это! Лучше всего было бы реализовать в каждом объекте правильное equals/gethashcode, так как по умолчанию без них вы можете получить много проблем. - person Fredou; 19.10.2016

Вам не нужно создавать для этого свой собственный универсальный метод. Вместо этого укажите пользовательский EqualityComparar для вашего типа данных:

var myDuplicates = myList.Distinct(new MyComparer());

Где вы определяете пользовательский Comparer следующим образом:

public class MyComparer : IEqualityComparer<Mine>
{
    public bool Equals(Mine x, Mine y)
    {
        if (x == null && y == null) return true;            
        if (x == null || y == null) return false;
        return x.Name == y.Name && x.Id == y.Id;
    }

    public int GetHashCode(Mine obj)
    {
        return obj.Name.GetHashCode() ^ obj.Id.GetHashCode();
    }
}

Изменить: изначально у меня был неверный код, он должен делать то, что вы хотите, без необходимости переопределять оператор Equals

person GEEF    schedule 19.10.2016
comment
если вы собираетесь показать реализацию Equals/GetHashCode, по крайней мере убедитесь, что делаете это правильно, Equals может вылететь с нулевым исключением, и вы забудете простое число в GetHashCode - person Fredou; 19.10.2016
comment
Простое число необходимо для правильной работы этой функции. Но уверен, что он определенно может рухнуть из-за NRE, хотя он может указать некоторые детали. - person GEEF; 19.10.2016