Подтвердить что ты не робот

Должна ли реализация IEqualityComparer.Equals допускать нулевые значения?

У меня есть настраиваемая общая структура данных, которая включает метод Find:

public bool Find(TValue value, IEqualityComparer<TValue> comparer)
{
    foreach (var x in items)
    {
        if (comparer.Equals(value, x))
            return true;
    }
    return false;
}

Недавно я получил сообщение от клиента, который сказал, что это приводит к тому, что его компаратор равен throw NullReferenceException, если value is null или если один из элементов в коллекции null.

Мой первоначальный ответ заключался в том, что его реализация IEqualityComparer<T>.Equals была ошибочной, потому что она не грамотно обрабатывает значения null. Но я не смог найти какую-либо документацию, чтобы явно поддержать меня. У меня есть некоторые доказательства, указывающие, что я прав, но ничего явно.

Во-первых, кажется глупым, что я бы изменил этот простой вызов comparer.Equals на:

if (x == null)
{
    if (value == null)
        return true;
}
else if (value != null && comparer.Equals(value, x))
    return true;

Во-вторых, документация для Object.Equals говорит, среди прочего:

  • x.Equals(null) возвращает false.
  • Реализации Equals не должны генерировать исключения.

Это, для меня, является убедительным доказательством того, что IEqualityComparer <T> .Equals должен изящно обрабатывать нулевые параметры.

Другие доказательства того, что документация для IComparer.Compare говорит:

Разрешается сопоставление нуля с любым ссылочным типом и не выполняется генерировать исключение. Нулевая ссылка считается меньшей, чем любая ссылка, которая не является нулевой.

Можно было бы ожидать, что IEqualityComparer<T>.Equals будет действовать аналогичным образом. Интересно отметить, однако, что пример, приведенный на этой странице, будет бросать NullReferenceException, если любой параметр null.

Я прошел через документацию для Object.Equals, IEquatable<T>, IEqualityComparer<T> и IEqualityComparer, а также бесчисленные сообщения в блогах, статьи и вопросы SO. Ничто не дает каких-либо конкретных рекомендаций о том, как обрабатывать параметры null.

Существуют ли такие руководящие принципы? Если нет, что рекомендуют гуру и почему?

4b9b3361

Ответ 1

Самый близкий метод в самой платформе .NET, после которого должны быть смоделированы все методы IEqualityComparer.Equals, - это статический метод Object.Equals(object,object). Согласно документации, этот метод грамотно обрабатывает null. Я думаю, что это дает достаточное представление о намерениях разработчиков .NET: IEqualityComparer.Equals также должен обрабатывать нули, и он должен обрабатывать их аналогичным образом (т.е. Обрабатывать два null как равные друг другу).

Ответ 2

Рекомендации, используемые FxCop, включают в себя указание, что каждый публичный метод открытого типа должен обрабатывать нулевые аргументы, например, бросая ArgumentNullException. В вашем случае, учитывая отмеченный Object.Equals, вам просто нужно выполнить нулевой тест и вернуть значение false, поскольку только null равно null:)

Это описано здесь: http://msdn.microsoft.com/en-us/library/ms182182(v=VS.80).aspx

Ответ 3

Ну, абстрактный базовый класс EqualityComparer<T> (а не интерфейс, но реализует его) имеет некоторые комментарии к его методам Equals.

В случае EqualityComparer<T>.Equals(T, T) MSDN не заявляет, что какие-либо известные исключения обычно выбрасываются (и MSDN обычно довольно хорош в отношении исключения исключений). Конечно, передача в любом классе (пользовательский или BCL) и сравнение его с null не вызывает никаких исключений.

http://msdn.microsoft.com/en-us/library/ms132154.aspx

Ответ 4

Значение null следует рассматривать как любое другое значение. Если вас спросят, следует ли считать содержимое двух ящиков одинаковым, вопрос совершенно верен, даже если один или оба поля пусты. Если оба поля пусты, их (не) содержимое равно. Если кто-то пуст, а другой нет, то они явно не полностью эквивалентны и, вероятно, не будут сравниваться одинаково, но бывают случаи, когда они законно могут.

Отношение эквивалентности по умолчанию, определяемое Object.Equals(Object) и IEquatable<T>.Equals(T) (последнее, если оно определено, должно использовать то же отношение, что и первое) должно рассматривать только вещи как равные, если они эквивалентны по существу всячески, и это определение, ни один ненулевой объект не может считаться эквивалентным нулевому (поскольку null не имеет возможности сравнивать себя с ненулевым объектом). С другой стороны, для IEqualityComparer<T> вполне нормально определять два экземпляра T как эквивалентные для своих целей, даже если они явно не совпадают, особенно если какая-то функция, вызванная на каждом из них, даст одинаковые стоимость. Если рассматриваемая функция рассматривает null как законное значение, то ненулевое значение, в котором функция возвращает то же значение, что и при null, должно сравниваться с null.