5 Stimmen

Lockerere" Eingabe in C# durch Abwärtsvererbung

Die Frage, die ich stellen möchte, ist folgende:

Ist Casting nach unten den Vererbungsbaum (dh. in Richtung eines more specialiased Klasse) von innerhalb einer abstrakten Klasse entschuldbar, oder sogar eine gute Sache, oder ist es immer eine schlechte Wahl mit besseren Optionen zur Verfügung?

Nun ein Beispiel dafür, warum ich glaube, dass sie zum Guten eingesetzt werden kann.

Ich habe vor kurzem Bencoding des BitTorrent-Protokolls in C#. Ein einfaches Problem, wie man die Daten darstellen kann. Ich habe mich für diesen Weg entschieden,

Wir haben eine abstract BItem Klasse, die einige grundlegende Funktionen bietet, darunter die static BItem Decode(string) die zur Dekodierung einer bencodierten Zeichenfolge in die erforderliche Struktur verwendet wird.

Außerdem gibt es vier abgeleitete Klassen, BString , BInteger , BList y BDictionary , die die vier verschiedenen zu kodierenden Datentypen darstellen. Jetzt kommt der schwierige Teil. BList y BDictionary haben this[int] y this[string] um den Zugriff auf die array-ähnlichen Eigenschaften dieser Datentypen zu ermöglichen.

Der potenziell schreckliche Teil kommt jetzt:

BDictionary torrent = (BDictionary) BItem.DecodeFile("my.torrent");
int filelength = (BInteger)((BDictionary)((BList)((BDictionary)
             torrent["info"])["files"])[0])["length"];

Nun, Sie verstehen, was ich meine... Autsch, das ist anstrengend für die Augen, ganz zu schweigen vom Gehirn. Also habe ich etwas Zusätzliches in die abstrakte Klasse eingeführt:

public BItem this[int index]
{
    get { return ((BList)this)[index]; }
}
public BItem this[string index]
{
    get { return ((BDictionary)this)[index]; }
}

Jetzt könnten wir den alten Code wie folgt umschreiben:

BDictionary torrent = (BDictionary)BItem.DecodeFile("my.torrent");
int filelength = (BInteger)torrent["info"]["files"][0]["length"];

Wow, hey presto, VIEL besser lesbarer Code. Aber habe ich gerade einen Teil meiner Seele verkauft, weil ich der abstrakten Klasse Kenntnisse über Unterklassen unterstellt habe?

EDIT: Als Antwort auf einige der eingegangenen Antworten sind Sie bei dieser speziellen Frage völlig auf dem Holzweg, da die Struktur variabel ist, zum Beispiel mein Beispiel torrent["info"]["files"][0]["length"] ist gültig, aber das gilt auch für torrent["announce-list"][0][0] und beides ist in 90 % der Torrent-Dateien vorhanden. Generics ist nicht der Weg zu gehen, mit diesem Problem zumindest :(. Klicken Sie sich durch die Spezifikation, die ich verlinkt, es ist nur 4 kleine Punkt-Punkte groß.

5voto

Rasmus Faber Punkte 47181

Ich denke, ich würde die accessors this[int] und this[string] virtuell machen und sie in BList/BDictionary überschreiben. Klassen, in denen die Accessoren keinen Sinn machen, sollten eine NotSupportedException() auslösen (vielleicht mit einer Standardimplementierung in BItem).

Dadurch funktioniert Ihr Code auf die gleiche Weise und Sie erhalten eine besser lesbare Fehlermeldung, falls Sie schreiben sollten

 (BInteger)torrent["info"][0]["files"]["length"];

aus Versehen.

3voto

petr k. Punkte 7910

Sie sollten wirklich nicht auf abgeleitete Klassen von der Basisklasse aus zugreifen, da dies die Idee von OOP ziemlich verletzt. Die Lesbarkeit ist sicherlich von großer Bedeutung, aber ich würde sie nicht gegen die Wiederverwendbarkeit eintauschen. Denken Sie an den Fall, dass Sie eine weitere Unterklasse hinzufügen müssen - Sie werden auch die Basisklasse entsprechend aktualisieren müssen.

1voto

RickL Punkte 2791

Wenn Dateilänge ist etwas, das Sie oft abrufen, warum nicht eine Eigenschaft in der BDictionary (?) Klasse implementieren ... so dass Sie Code wird:

BDictionary torrent = BItem.DecodeFile("my.torrent");
int filelength = torrent.FileLength;

Auf diese Weise bleiben die Implementierungsdetails für den Benutzer verborgen.

1voto

neaorin Punkte 183

So wie ich es sehe, sind nicht alle BItems Sammlungen, also haben nicht alle BItems Indexer, also sollte der Indexer nicht in BItem sein. Ich würde eine andere abstrakte Klasse von BItem ableiten, nennen wir sie BCollection, und die Indexer dort unterbringen, etwa so:

abstract class BCollection : BItem {

      public BItem this[int index] {get;}
      public BItem this[string index] {get;}
}

und machen BList und BDictionary zu Erben von BCollection. Oder Sie könnten einen Schritt weiter gehen und BCollection zu einer generischen Klasse machen.

1voto

Thomas Eyde Punkte 3702

Meine Empfehlung wäre, mehr Abstraktionen einzuführen. Ich finde es verwirrend, dass ein BItem eine DecodeFile() hat, die ein BDictionary zurückgibt. Das mag im Torrent-Bereich sinnvoll sein, ich weiß es nicht.

Ich würde jedoch eine API wie die folgende für sinnvoller halten:

BFile torrent = BFile.DecodeFile("my.torrent");
int filelength = torrent.Length;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X