Самый простой способ думать об интерфейсах - это распознать, что означает наследование. Если класс CC наследует класс C, это означает, что:
- Класс CC может использовать любые открытые или защищенные члены класса C, как если бы они были его собственными, и, таким образом, ему нужно только реализовать вещи, которых нет в родительском классе.
- Ссылка на CC может быть передана или присвоена подпрограмме или переменной, которая ожидает ссылку на C.
Эти две функции наследования в некотором смысле независимы; хотя наследование применяется оба одновременно, также возможно применить второе без первого. Это полезно, потому что позволить объекту наследовать члены от двух или более несвязанных классов намного сложнее, чем допустить замену одного типа вещей несколькими типами.
Интерфейс чем-то напоминает абстрактный базовый класс, но с ключевым отличием: объект, который наследует базовый класс, не может наследовать ни один другой класс. Напротив, объект может реализовывать интерфейс, не влияя на его способность наследовать любой желаемый класс или реализовывать любые другие интерфейсы.
Одна приятная особенность этого (недостаточно используемая в .net framework, IMHO) заключается в том, что они позволяют декларативно указывать, что может делать объект. Некоторым объектам, например, понадобится объект источника данных, из которого они могут извлекать вещи по индексу (как это возможно в List), но им не нужно ничего там хранить. Другие подпрограммы будут нуждаться в объекте хранилища данных, где они могут хранить вещи не по индексу (как в Collection.Add), но им не нужно будет что-либо читать обратно. Некоторые типы данных разрешают доступ по индексу, но не разрешают запись; другие позволят писать, но не разрешат доступ по индексу. Некоторые, конечно, позволят и то и другое.
Если бы ReadableByIndex и Appendable были несвязанными базовыми классами, было бы невозможно определить тип, который можно было бы передать как вещам, ожидающим ReadableByIndex, так и вещам, ожидающим Appendable. Можно попытаться смягчить это, используя ReadableByIndex или Appendable, производные от другого; производный класс должен был бы сделать доступными открытые члены для обеих целей, но предупредите, что некоторые открытые члены могут фактически не работать. Некоторые из классов и интерфейсов Microsoft делают это, но это довольно странно. Более чистый подход состоит в том, чтобы иметь интерфейсы для различных целей, а затем объекты должны реализовывать интерфейсы для того, что они действительно могут делать. Если бы у одного был интерфейс IReadableByIndex, а у другого интерфейса IAppendable, классы, которые могли бы делать один или другой, могли бы реализовать соответствующие интерфейсы для вещей, которые они могут делать.