В самых основных терминах мы возвращаемся к ООП 101:
Наследование: Объект B "является" типом Объекта A. Поведения и методы, реализованные Объектом A, наследуются, реализация и все (с некоторым пространством для Переопределения) Объектом B.
Интерфейс: Объект A и Объект B представляют собой примеры "Действуй как" абстрактного объекта, представленного общим интерфейсом. Джеймс Гонт использует приведенный выше пример. Другими примерами могут быть IPrintable, IDisposable и т. Д.
Для любого данного класса, который реализует эти Интерфейсы, реализация может сильно отличаться (подумайте о том, как вы внедряете IDisposable в разные классы, которые используют метод dispose). Однако клиентскому коду не нужно знать или заботиться о том, что является типом фактического объекта - код может просто получить доступ к нужным свойствам и методам через интерфейс.
Наследование часто рассматривается как «волшебный» ответ на многие проблемы кодирования, но также широко используется как средство, позволяющее избежать переписывания большего количества кода. Я не согласен с user492238, что все, что можно сделать с помощью интерфейсов, можно легко сделать с помощью наследования. Такой подход часто загоняет вас в угол. И, как замечает Джодрелл, множественное наследование не является особенностью .net (и, на мой взгляд, справедливо).
Когда вы обнаруживаете, что реализуете одно и то же поведение в нескольких (или во многих) не связанных между собой классах, рассмотрите возможность определения интерфейса, который предоставляет API для этого поведения.
У вас может быть несколько классов: Person, Animal, Building и т. Д. Все из них могут требовать метод для вывода на печать. У вас также может быть метод, который принимает IPrintableObject в качестве параметра. В этом случае вы можете реализовать IPrintableObject в любом из классов, которые необходимо распечатать, предоставить код реализации в каждом из этих объектов и передать их клиентскому коду.