Должна ли реализация 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
.
Существуют ли такие руководящие принципы? Если нет, что рекомендуют гуру и почему?
Ответы
Ответ 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
.