Comments 13
Type-specific сравнение объектов по значению позволяет достичь [...] Более высокой производительности.
Я надеюсь, у вас есть конкретные бенчмарки в пользу этого утверждения?
Кроме того, реализация Type-specific сравнения по значению необходима по причинам:
Стандартные Generic-коллекции (List(Ot T), LinkedList(Of T), Dictionary(Of TKey, TValue) и др.) требуют реализацию IEquatable(Of T) для всех объектов, хранимых в коллекциях.
Нет.
List<T>
:T
неограниченLinkedList<T>
:T
неограниченDictionary<TKey, TValue>
: ниTKey
, ниTValue
не ограничены
Стандартный компаратор-по-значению EqualityComparer(Of T) по умолчанию использует реализацию IEquatable(Of T) у операндов.
Вы, наверное, имеете в виду EqualityComparer<T>.Default
. В таком случае, ваше утверждение верно только частично. Действительно, если T
в EqualityComparer<T>.Default
реализует IEquatable<T>
, то компаратор будет использовать эту реализацию (через GenericEqualityComparer<T>
). Однако, T
не обязан реализовывать IEquatable<T>
— в этом случае будет использован ObjectEqualityComparer<T>
.
Таким образом, хотя реализация IEquatable<T>
и имеет некоторые преимущества, для указанных вами сценариев она не обязательна.
List(Of T): T неограничен
LinkedList(Of T): T неограничен
Dictionary(Of TKey, TValue): ни TKey, ни TValue не ограничены
Верно, не требуют обязательного IEquatable(Of T), т.к. «It should be implemented for any object that might be stored in a generic collection.»,
а не «It must be implemented».
Стоит уточнить формулировку в статье.
Вы, наверное, имеете в виду EqualityComparer(Of T).Default. В таком случае, ваше утверждение верно только частично. Действительно, если T в EqualityComparer(Of T).Default реализует IEquatable(Of T), то компаратор будет использовать эту реализацию (через GenericEqualityComparer(Of T)). Однако, T не обязан реализовывать IEquatable(Of T) — в этом случае будет использован ObjectEqualityComparer(Of T).
«по умолчанию» относится к «по умолчанию использует реализацию», т.е. когда есть Generic-реализация.
а не «компаратор по умолчанию».
Требуется уточнение формулировки в статье.
Я надеюсь, у вас есть конкретные бенчмарки в пользу этого утверждения?Давайте определимся — ранее, сейчас и далее, мы говорим о контрактах/спецификациях, а не конкретных реализациях, которые могут быть оптимизированы, в т.ч. на уровне JIT и т.д.
Из двух методов Equals, где заведомо все строки совпадают, но в одном есть дополнительное приведение через as, один будет заведомо быстрее другого. Насколько — другой вопрос.
То же касается и случая, когда хеш-контейнер для входящего объекта проверяет вначале наличие Generic-метода Equals, и только затем Object-метода.
(Хотя последний пример — зависит от реализации контейнера, но можно предполагать, что стандартные контейнеры вначале будут проверять Generic-версию.)
мы говорим о контрактах/спецификациях
В контракте и/или спецификации есть какое-либо конкретное утверждение, подтверждающее ваш тезис?
Из двух методов Equals, где заведомо все строки совпадают, но в одном есть дополнительное приведение через as, один будет заведомо быстрее другого.
… если не учитывать накладные расходы на остальные операции. Именно поэтому важны бенчмарки, а не теоретические рассуждения о "будет заведомо быстрее".
хеш-контейнер для входящего объекта проверяет вначале наличие Generic-метода Equals, и только затем Object-метода. [...] можно предполагать, что стандартные контейнеры вначале будут проверять Generic-версию.
Нет. Стандартные контейнеры достают все тот же EqualityComparer<T>.Default
, который и используют впоследствии. Так что там есть однократная потенциальная потеря в момент создания компарера, но в операциях работы с элементами разницы нет.
Статический protected метод-хелпер EqualsHelper(Person, Person) сравнивает два объекта по полям, сочетание значений которых образует уникальность значения конкретного объекта.
Совершенно не понятно, зачем он такой нужен, когда можно просто использовать Equals(Person)
.
Я бы понял, если бы он проверки на null
делал, но он же ожидает не-null
объекты с обеих стороны. И вызываете вы его ровно в одном месте.
Аналогично, я бы понял, если бы вы его вызывали отовсюду, откуда можно, вместо a.Equals(b)
, чтобы получить call
вместо callvirt
(но тут возникают сложности с полиморфизмом, но они у вас и так возникают пока).
Совершенно не понятно, зачем он такой нужен, когда можно просто использовать Equals(Person).
И вызываете вы его ровно в одном месте.
Он будет использоваться при наследовании (можно посмотреть в предыдущей статье, как он используется).
Но использовать его нужно точечно и аккуратно, т.к. верно, в данном случае с полиморфизмом сложности.
реализован статический метод Person.Equals(Person, Person).
Отдельный задорный вопрос. Как должен себя вести этот метод, когда у нас есть PersonEx: Person
, имеющий свой собственный PersonEx.Equals(PersonEx, PersonEx)
? Должна ли для пользователя быть разница между PersonEx.Equals(pex1, pex2)
и Person.Equals(pex1, pex2)
?
Почему-то возникает стойкое ощущение спама статьями качества ниже среднего ради высокого рейтинга здесь и сейчас.
О сравнении объектов по значению — 3, или Type-specific Equals & Equality operators