Was soll ich in die ?? Objekte erben, die von Animal
und IHasLegs
erben? Ich möchte kein Snake
in diesem Käfig sehen und auch kein Table
.
-------------- BEARBEITEN --------------
Vielen Dank für Ihre Antworten, aber hier ist die Sache: Was ich eigentlich machen möchte, ist folgendes:
%Vor%TextBox / ComboBox ist natürlich ein Control. Jetzt, wenn ich eine abstrakte Klasse erstelle, die sowohl Control als auch IClonable erbt, verliere ich die TextBox / ComboBox-Vererbung, die ich brauche. Mehrfache Klassenvererbung ist nicht erlaubt, daher muss ich mit Schnittstellen arbeiten. Jetzt, wo ich wieder darüber nachdenke, könnte ich eine andere Schnittstelle erstellen, die von IClonable erbt:
%Vor%und dann
%Vor%Danke !!
Zunächst ist Animals
eine schlechte Wahl für einen Klassennamen, es wäre Animal
. Klassennamen sollten in Singular sein. Diese Klasse sollte auch als abstract
deklariert werden, da sie nur eine Basisklasse für konkrete Typen wie Donkey
ist.
Zweitens können Sie eine abstrakte Klasse namens LeggedAnimal
definieren, die von Animal
und IHasLegs
erbt. Und dann können Sie Donkey
von LeggedAnimal
usw. erben.
Schließlich kannst du dann List<LeggedAnimal>
sagen und du kannst loslegen!
Es gibt kein direktes Konzept von List<T where T : Animals, IHasLegs>
. Sie können die T
um eine Stufe in den Käfig verschieben - aber dann muss der Aufrufer eine einzelne T
angeben, die beide Bedingungen erfüllt:
Es könnte zum Beispiel ein Cage<Lizard>
sein - oder (separat) ein Cage<Donkey>
- aber Sie konnten dies trotzdem nicht zum Speichern von any verwenden Animal
das hat Beine - dh du könntest kein Lizard
und kein Donkey
im gleichen Käfig mit diesem Konzept setzen.
Ein Ansatz, wenn es eine vernünftige Anzahl von Kombinationen von Typen gibt, an denen Sie interessiert sind, besteht darin, zusammengesetzte Schnittstellen zu definieren, die von mehreren Schnittstellen erben. Man kann eine zusammengesetzte Schnittstelle für jede Kombination von Schnittstellen definieren und einen solchen Typ als generischen Parameter verwenden. Das größte Problem bei diesem Ansatz ist, dass es keine Möglichkeit gibt, dies zu spezifizieren, auch wenn eine Schnittstelle wie IFooBar
einfach IFoo
und iBar
ist und keine eigenen Mitglieder deklariert, also jede Klasse, die IFoo
und% co_de implementiert % implementiert alle Mitglieder von IBar
, die nicht zulassen, dass solche Klassen in IFooBar
umgewandelt werden, es sei denn, sie deklarieren sich als Implementierung. Folglich muss man entscheiden, welche Kombinationen von Schnittstellen man interessiert, bevor man Klassen definiert, die diese Kombinationen von Schnittstellen implementieren, wenn diese Klassen durch Code verwendbar sein sollen, der diese Kombinationen erwartet.
Ein alternativer Ansatz besteht darin, eine Schnittstelle IFooBar
zu definieren und für viele Ihrer anderen Schnittstellen (z. B. ISelf<out T> { T Self {get;}}
) auch z. IAnimal
und einen beliebigen Typ, z. IAnimalAnd<out T> : IAnimal, ISelf<T> { }
, die z.B. Zebra
implementiert auch IAnimal
. Wenn man dieses Muster verwendet und eine Liste von IAnimalAnd<Zebra>
benötigt, von denen bekannt ist, dass sie Animal
, IWagTail
und IMoo
unterstützen, dann wird% ce_de%, das diese Dinge gemäß dem Muster tut, IGiveMilk
( man kann die Schnittstellen in beliebiger Reihenfolge verschachteln). Wenn man eine Referenz Animal
dieses Typs hat, wird IWagTailAnd<IMooAnd<IGiveMilkAnd<Animal>>>>
it
implementieren, it
wird IWagTail
implementieren, it.Self
wird IMoo
implementieren, und it.Self.Self
wird IGiveMilk
. Wenn it.Self.Self.Self
das Muster in der üblichen Weise implementiert, beziehen sich alle oben genannten Referenzen auf die gleiche Objektinstanz Animal
, aber als unterschiedliche Typen.
Was bei diesem Ansatz hilfreich ist, ist, dass ein Objekt, das eine beliebige Kombination von Schnittstellen unter Verwendung dieses Musters implementiert, in verschachtelten Schnittstellentypen umgewandelt werden kann, die eine Teilmenge dieser Typen darstellen, unabhängig davon, ob irgendjemand dachte, dass eine bestimmte Teilmenge beim Typ nützlich wäre wurde geschrieben. Die größte Schwierigkeit bei diesem Ansatz besteht darin, dass, obwohl ein Objekt, das it
implementiert, sowohl it
als auch IWagTailAnd<IGiveMilkAnd<Animal>>
erwartet, der Compiler das nicht erkennen kann; es kann sagen, dass das Objekt IWagTail
implementiert, und dass seine IGiveMilk
-Eigenschaft IWagTail
implementiert und man könnte erwarten, dass bei normalen Implementierungen des Musters die Self
-Eigenschaft des Objekts auf sich selbst verweist (was bedeutet, dass es implementiert werden muss) IGiveMilk
), aber der Compiler kann das nicht wissen.
Tags und Links c# interface declaration encapsulation