Использование Linq, но не работает, как я думал

List1 содержит элементы { A, B }, а List2 содержит элементы { A, B, C }.

Мне нужно, чтобы возвращалось { C }, когда я использую расширение Except Linq. Вместо этого мне возвращается { A, B }, и если я переворачиваю списки в своем выражении, результат будет { A, B, C }.

Я неправильно понимаю смысл Except? Есть ли другое расширение, которое я не вижу для использования?

Я просмотрел и попробовал несколько разных сообщений по этому вопросу, но пока безуспешно.

var except = List1.Except(List2); //This is the line I have thus far

РЕДАКТИРОВАТЬ: Да, я сравнивал простые объекты. Никогда не пользовался IEqualityComparer, было интересно узнать.

Спасибо всем за помощь. Проблема заключалась не в реализации компаратора. Связанный пост в блоге и пример ниже, где это полезно.


person Schanckopotamus    schedule 29.05.2013    source источник
comment
Что конкретно в этих списках?   -  person Ufuk Hacıoğulları    schedule 30.05.2013
comment
Каков тип данных ваших элементов. Это класс? Эта ссылка может помочь вам: используя iequatable"> stackoverflow.com/questions/1645891/   -  person arunlalam    schedule 30.05.2013
comment
На данный момент это простые объекты с парой свойств. Я посмотрю вашу ссылку.   -  person Schanckopotamus    schedule 30.05.2013


Ответы (5)


Если вы храните ссылочные типы в своем списке, вы должны убедиться, что есть способ сравнить объекты на равенство. В противном случае они будут проверены путем сравнения, ссылаются ли они на один и тот же адрес.

Вы можете реализовать IEqualityComparer<T> и отправить его в качестве параметра Except(). функция. Вот блог сообщение может оказаться полезным.

изменить: ссылка на исходный пост в блоге не работает и заменена выше

person Ufuk Hacıoğulları    schedule 29.05.2013
comment
Чтобы было ясно, это решение, которое давно решило мою проблему, и является жизнеспособным вариантом, в зависимости от вашего сценария. - person Schanckopotamus; 30.12.2014
comment
У меня есть List<MyType>, а MyType реализует IEquatable<T>. Однако, когда я использую метод Except, я все равно получаю то, что ОП называет { A, B, C }. В чем проблема? - person Sipo; 19.03.2019
comment
Очень жаль, что статья оформлена не очень красиво. - person Dan Cundy; 02.04.2020

Так что для полноты...

// Except gives you the items in the first set but not the second
    var InList1ButNotList2 = List1.Except(List2);
    var InList2ButNotList1 = List2.Except(List1);
// Intersect gives you the items that are common to both lists    
    var InBothLists = List1.Intersect(List2);

Редактировать: поскольку ваши списки содержат объекты, вам необходимо передать их в IEqualityComparer для вашего класса... Вот как будет выглядеть ваше исключение с образцом IEqualityComparer на основе созданных объектов... :)

// Except gives you the items in the first set but not the second
        var equalityComparer = new MyClassEqualityComparer();
        var InList1ButNotList2 = List1.Except(List2, equalityComparer);
        var InList2ButNotList1 = List2.Except(List1, equalityComparer);
// Intersect gives you the items that are common to both lists    
        var InBothLists = List1.Intersect(List2);

public class MyClass
{
    public int i;
    public int j;
}

class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
    public bool Equals(MyClass x, MyClass y)
    {
        return x.i == y.i &&
               x.j == y.j;
    }

    public int GetHashCode(MyClass obj)
    {
        unchecked
        {
            if (obj == null)
                return 0;
            int hashCode = obj.i.GetHashCode();
            hashCode = (hashCode * 397) ^ obj.i.GetHashCode();
            return hashCode;
        }
    }
}
person Kevin    schedule 29.05.2013
comment
InList1ButNotList2 дает {C, B, A}, что и происходит. - person Schanckopotamus; 30.05.2013
comment
@Schanckopotomus Ааа ... ваши списки - это объекты, и их свойства равны, но объекты не равны по ссылкам, верно? - person Kevin; 30.05.2013

Вы просто перепутали порядок аргументов. Я понимаю, откуда возникла эта путаница, потому что официальная документация не так полезна, как могла бы. быть:

Производит установленную разницу двух последовательностей, используя компаратор равенства по умолчанию для сравнения значений.

Если вы не разбираетесь в теории множеств, вам может быть неясно, в чем на самом деле заключается различие множества. Это не просто в чем разница между наборами. На самом деле Except возвращает список элементов первого набора, которых нет во втором наборе.

Попробуй это:

var except = List2.Except(List1); // { C }
person p.s.w.g    schedule 29.05.2013
comment
Если изменить порядок списка, я получаю {A, B, C} - person Schanckopotamus; 30.05.2013

Написание собственного компаратора, похоже, решает проблему, но я думаю, что https://stackoverflow.com/a/12988312/10042740 гораздо более простое и элегантное решение.

Он перезаписывает методы GetHashCode() и Equals() в вашем классе, определяющем объект, тогда компаратор по умолчанию делает свое волшебство без лишнего кода, загромождающего место.

person Etienne Redelinghuys    schedule 11.07.2020

Только для справки: я хотел сравнить USB-накопители, подключенные и доступные для системы.

Итак, это класс, который реализует интерфейс IEqualityComparer.

public class DriveInfoEqualityComparer : IEqualityComparer<DriveInfo>
{
    public bool Equals(DriveInfo x, DriveInfo y)
    {
        if (object.ReferenceEquals(x, y))
            return true;
        if (x == null || y == null)
            return false;
        // compare with Drive Level
        return x.VolumeLabel.Equals(y.VolumeLabel);
    }

    public int GetHashCode(DriveInfo obj)
    {
        return obj.VolumeLabel.GetHashCode();
    }
}

и вы можете использовать его так

var newDeviceLst = DriveInfo.GetDrives()
                            .ToList()
                            .Except(inMemoryDrives, new DriveInfoEqualityComparer())
                            .ToList();
person Sumit    schedule 21.09.2018