Абстрактный класс или интерфейс?
Чем они отличаются между собой и в чём отличия?
В плане синтаксиса, интерфейс может содержать внутри себя только методы без реализации, свойства, события. В то время как абстрактный класс поддерживает функционал любого другого класса(поля, реализованные методы, делегаты, события, свойства, конструкторы...), но запрещает создавать экземпляры своего типа. Также нужно помнить, что C# не поддерживает множественное наследование и, соответственно, унаследоваться от нескольких классов не получиться, а вот от нескольких интерфейсов - да.
В плане логики, интерфейсы чаще используют, чтобы придать классу некоторую функциональность, по которой его в дальнейшем можно определять. К примеру, foreach можно применять по отношению к типам, в которых реализован интерфейс IEnumerbale.
Вот пару советов по определению нужного механизма:
- Связь потомка с предком. Любой тип может наследовать только одну реализацию. Если производный тип не может ограничиваться отношением типа «является частным случаем» с базовым типом, нужно применять интерфейс, а не базовый тип. Интерфейс подразумевает отношение «поддерживает функциональность». Например, тип может преобразовывать экземпляры самого себя в другой тип (IConvertible), может создать набор экземпляров самого себя (ISerializable) и т. д. Заметьте, что значимые типы должны наследовать от типа System.ValueType и поэтому не могут наследовать от произвольного базового класса. В этом случае нужно определять интерфейс.
- Простота использования. Разработчику проще определить новый тип, производный от базового, чем создать интерфейс. Базовый тип может предоставлять массу функций, и в производном типе потребуется внести лишь незначительные изменения, чтобы изменить его поведение. При создании интерфейса в новом типе придется реализовывать все члены.
- Четкая реализация. Как бы хорошо ни был документирован контракт, вряд ли будет реализован абсолютно корректно. По сути, проблемы COM связаны именно с этим — вот почему некоторые COM-объекты нормально работают только с Microsoft Word или Microsoft Internet Explorer. Базовый тип с хорошей реализацией основных функций — прекрасная отправная точка, вам останется изменить лишь отдельные части.
- Управление версиями. Когда вы добавляете метод к базовому типу, производный тип наследует стандартную реализацию этого метода без всяких затрат. Пользовательский исходный код даже не нужно перекомпилировать. Добавление нового члена к интерфейсу требует изменения пользовательского исходного кода и его перекомпиляции.
Наконец, нужно сказать, что на самом деле можно определить интерфейс и создать базовый класс, который реализует интерфейс. Например, в FCL определен интерфейс IComparer<T>, и любой тип может реализовать этот интерфейс. Кроме того, FCL предоставляет абстрактный базовый класс Comparer<T>, который реализует этот интерфейс (абстрактно) и предлагает реализацию по умолчанию для необобщенного метода Compare интерфейса IComparer. Применение обеих возможностей дает большую гибкость, поскольку разработчики теперь могут выбрать из двух вариантов наиболее предпочтительный.