Джейсон рассмотрел основы - используйте абстрактный класс, где вы хотите поделиться кодом, в противном случае используйте интерфейс.
Я хочу добавить одну точку - даже если вы пойдете по пути абстрактного класса, я все равно рекомендую создать интерфейс. Вы никогда не знаете, когда вы захотите иметь подкласс, который действует как другие, но по какой-то причине ему действительно нужно наследовать другой класс. Абстрактные базовые классы отлично подходят для сохранения кода реализации. Тем не менее, интерфейсы более гибкие, поэтому, если нет веских причин (т. Е. Ваши тесты показывают, что вы не можете снизить производительность вызовов виртуальных методов), используйте интерфейс, а также абстрактный базовый класс, где это имеет смысл.
Мое общее правило по этому поводу: используйте интерфейсы для определения API и используйте абстрактные базовые классы, чтобы позволить реализациям интерфейса совместно использовать код.