Есть ли у кого-нибудь мнения о том, требуется ли вообще IEquatable<T>
или IComparable<T>
, чтобы T
был sealed
(если он a class
)?
Этот вопрос возник со мной, так как я пишу набор базовых классов, предназначенных для помощи в реализации неизменяемых классов. Часть функциональности, которую должен обеспечить базовый класс, - автоматическая реализация сравнений равенства (с использованием полей класса вместе с атрибутами, которые могут применяться к полям для контроля сравнений равенства). Это должно быть довольно хорошо, когда я закончен - я использую деревья выражений для динамического создания скомпилированной функции сравнения для каждого T
, поэтому функция сравнения должна быть очень близка к производительности регулярной функции сравнения равенства. (Я использую неизменяемый словарь с ключом System.Type
и дважды проверяю блокировку, чтобы хранить сгенерированные функции сравнения так, чтобы это было разумно выполнено)
Однако одна вещь, которая возникла, - это то, какие функции использовать для проверки равенства полей-членов. Мое первоначальное намерение состояло в том, чтобы проверить, реализует ли тип поля каждого члена (который я назовем X
) IEquatable<X>
. Однако, подумав, я не думаю, что это безопасно использовать, если X
не является sealed
. Причина в том, что если X
не sealed
, я не уверен, что если X
надлежащим образом делегирует проверки равенства виртуальному методу на X
, тем самым позволяя подтипу переопределить сравнение равенства.
В этом случае возникает более общий вопрос: если тип не запечатан, должен ли он действительно реализовать эти интерфейсы НА ВСЕ? Я бы не подумал, так как я бы сказал, что контракт с интерфейсом заключается в сравнении двух типов X
, а не двух типов, которые могут быть или не быть X
(хотя они должны быть, конечно, X
или подтипом).
Что вы, ребята, думаете? Следует ли избегать IEquatable<T>
и IComparable<T>
для незапечатанных классов? (Также заставляет задуматься, есть ли для этого правило fxcop)
Моя текущая мысль заключается в том, чтобы моя сгенерированная функция сравнения использовала только IEquatable<T>
в полях-членах, T
- sealed
, а вместо этого использовать виртуальный Object.Equals(Object obj)
, если T
не распечатано, даже если T
реализует IEquatable<T>
, так как поле может потенциально хранить подтипы T
, и я сомневаюсь, что большинство реализаций IEquatable<T>
спроектированы надлежащим образом для наследования.