Вам нужны интерфейсы, когда вы хотите представить себе разнородный набор классов как объекты одного типа. Например, у вас есть набор классов, которые все читают свою конфигурацию из файла. Один из способов справиться с этим - заставить все классы реализовать соответствующие методы для чтения конфигурации из файла. Проблема в том, что тогда любой код, который использует эти классы и хочет их настроить, должен знать обо всех различных классах, чтобы он мог использовать методы для них.
Другой способ состоит в том, чтобы все они были производными от одного базового класса. Таким образом, любой код, использующий классы, должен знать только о базовом классе - он может рассматривать любой из производных классов как базовый класс и использовать те методы, которые определены в базовом классе, для настройки. Это не так уж плохо, но у него есть главный недостаток - поскольку C # не поддерживает множественное наследование - ограничение вашей цепочки наследования. Если вы хотите, чтобы у некоторых классов была одинаковая способность, но не у всех для различного набора поведения, вы все равно застряли в реализации этого для всех. Не хорошо.
Последний способ - использовать интерфейсы для определения поведения. Любой класс, желающий реализовать поведение, должен только реализовать интерфейс. Любой класс, желающий использовать это поведение, должен знать только об интерфейсе и затем может использовать любой класс, который его реализует. Классы могут реализовывать любой интерфейс или даже несколько интерфейсов, так что вы можете детально распределить поведение между классами. Вы по-прежнему можете использовать базовые классы и иерархии наследования, чтобы обеспечить общее поведение класса совместно, но этот класс также может свободно реализовывать другие интерфейсы, чтобы обеспечить себе большее поведение и при этом сохранять удобство классов, которые используют его, чтобы знать только об интерфейсе.