In den grundlegendsten Begriffen kommen wir zurück zum OOP 101:
Vererbung: Objekt B "ist" ein Typ von Objekt A. Die von Objekt A implementierten Verhaltensweisen und Methoden werden von Objekt B geerbt, und zwar mit allen Implementierungen (mit etwas Spielraum für Overriding).
Schnittstelle: Objekt A und Objekt B "verhalten sich wie" Beispiele eines abstrakten Objekts, das durch eine gemeinsame Schnittstelle dargestellt wird. James Gaunt verwendet das obige Beispiel von Ienumerable. Andere Beispiele könnten IPrintable, IDisposable, etc. sein.
Für jede beliebige Klasse, die diese Schnittstellen implementiert, kann die Implementierung sehr unterschiedlich sein (denken Sie daran, wie Sie IDisposable in verschiedenen Klassen implementieren, die eine Dispose-Methode verwenden). Der Client-Code braucht jedoch nicht zu wissen oder sich darum zu kümmern, was der eigentliche Typ des Objekts ist - der Code kann einfach über die Schnittstelle auf die gewünschten Eigenschaften und Methoden zugreifen.
Vererbung wird oft als "magische" Antwort auf eine Vielzahl von Programmierproblemen angesehen, wird aber auch häufig als Mittel missbraucht, um das Schreiben von mehr Code zu vermeiden. Ich stimme nicht mit user492238 überein, dass Dinge, die mit Interfaces gemacht werden, genauso gut durch Vererbung erledigt werden können. Mit einem solchen Ansatz wird man oft in die Enge getrieben. Und, wie Jodrell bemerkt, ist Mehrfachvererbung kein Merkmal von .net (und das meiner Meinung nach zu Recht).
Wenn Sie dasselbe Verhalten in mehreren (oder vielen) ansonsten nicht verwandten Klassen implementieren müssen, sollten Sie eine Schnittstelle definieren, die die API für dieses Verhalten bereitstellt. Sie haben vielleicht mehrere Klassen: Person, Tier, Gebäude, usw. Alle benötigen möglicherweise eine Methode, um eine druckbare Ausgabe zu liefern. Sie können auch eine Methode haben, die IPrintableObject als Parameter akzeptiert. In diesem Fall können Sie IPrintableObject in jeder der zu druckenden Klassen implementieren, den Implementierungscode in jedem dieser Objekte bereitstellen und sie an den Client-Code weiterleiten.