2016-07-12 4 views
-2

У меня есть кусок кода, который просто сравнивает две коллекции и возвращает все элементы из одного списка, который не принадлежит другому.Немедленное окно и среда выполнения, дающая разные результаты для одного оператора LINQ

Поскольку эти два списка содержат объекты, которые не являются справедливыми по ссылке, у меня есть простой IEquatable, который сравнивает объекты с их идентификаторами.

код Я бегу следующим образом:

private PreferenceDefinition[] FindUserPreferencesToAdd(PreferenceDefinition[] newDefinitions, PreferenceDefinition[] oldDefinitions) 
{ 
    //Get the newly selected definitions 
    var newPreferences = newDefinitions.Where(def => def.IsSelected); 

    //Get all new definitions that don't exist in the old list 
    var preferencesToAdd = newPreferences.Where(def => !oldDefinitions.Contains(def)).ToArray(); 

    return preferencesToAdd; 
} 

Результат preferencesToAdd дает мне точно такой же список в newPreferences, несмотря на обеспечение того, чтобы намеренно newDefinitions содержит дополнительный элемент, который был выбран. Если я перейду в 7 новых предпочтений, он вернет 7 предпочтений для «добавления» - неправильной реализации.

Однако, когда я бегу точно такое же заявление LINQ в Immediate Window, когда я достиг точки останова на возвращение, он дает мне:

newPreferences.Where(def => !oldDefinitions.Contains(def)).ToArray(); 
{App1Test.PreferenceDefinition[1]} 
    [0]: {App1Test.PreferenceDefinition} 

Это содержит единственный результат, который должен быть добавлен.

Зачем нужно немедленно отображать верный результат, но код времени выполнения не будет? Я попытался запустить этот оператор в непосредственном окне до и после выполнения запроса LINQ для выполнения, чтобы убедиться, что это не порядок операций, но это не имело никакого значения.

Edit:

Я нашел решение моей проблемы, заменив второе заявление LINQ с:

var preferencesToAdd = newPreferences.Where(d => !oldDefinitions.Any(def => def.Equals(d))).ToArray();

Однако я не понимаю, почему оригинал не работает. У меня очень похожий сценарий, который делает противоположное тому, что я пытаюсь достичь здесь (найти элементы для удаления), и он отлично работает. Я что-то упускаю?

Edit 2:

Этот код, даже без IEquatable, работал отлично:

private PreferenceDefinition[] FindUserPreferencesToDelete(PreferenceDefinition[] newDefinitions, PreferenceDefinition[] oldDefinitions) 
{ 
    //Get the newly selected definitions 
    var newPreferences = newDefinitions.Where(def => def.IsSelected); 

    //Get all old definitions that don't exist in this new list 
    var preferencesToDelete = oldDefinitions.Where(def => !newPreferences.Contains(def)); 

    return preferencesToDelete.ToArray(); 
} 

Почему это будет работать хорошо, но первый метод не будет?

+0

Показать свою реализацию IEquatabe. –

+0

@HamletHakobyan Это просто 'public bool Equals (PreferenceDefinition other) { return this.Id == other.Id; } ' – plusheen

+1

@plusheen: Просто в стороне, но ваша реализация' Equals' должна изящно иметь дело с возможностью 'other' быть' null'. – sstan

ответ

2

Посмотрите здесь:

https://msdn.microsoft.com/en-us/library/ms131187(v=vs.110).aspx

Если вы реализуете IEquatable, вы должны также переопределить реализацию базового классовые Object.equals (Object) и GetHashCode так, что их поведение соответствует с методом IEquatable.Equals .

Вы можете ясно вычитать, что == оператор не зависит от реализации IEquatable;

Поэтому ваш Равно вызов использует свой метод пользовательского равенства и Содержит внутренне использовать Object.Equals который не переопределен.

Массив реализует ICollection поэтому Содержит метод, используемый внутри реализован в массиве с IndexOf без использования EqualityComparer.Default, который пытается использовать IEquatable.Equals, если таковые имеются.

Попробуйте добавить это:

public override bool Equals(object obj) 
{ 
    var test = obj as Test; 

    return test == null ? obj.Equals(this) : Equals(test); 
} 

Вы можете увидеть простой пример здесь: https://dotnetfiddle.net/C5R4bE

+0

@sstan нет, это не так. Протестируйте его для себя, а затем удалите бессмысленный нижний план. –

+0

Моя ошибка. Я просто заметил, что реализация 'Enumerable.Contains' использует' IEquatable ', но только если' IEnumerable 'is * not * a' ICollection '. Если это 'ICollection ', то он принимает другой путь, который ведет себя так, как вы описали. – sstan

+0

@sstan Точно; но я согласен, что это вводит в заблуждение. –

Смежные вопросы