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ß.

0voto

Stormenet Punkte 24616

Haben Sie daran gedacht, einen einfachen "Pfad" zu analysieren, damit Sie ihn so schreiben können?

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

Vielleicht nicht der beste Weg, aber die Lesbarkeit erhöht sich (ein wenig)

0voto

Gishu Punkte 130442
  • Wenn Sie die vollständige Kontrolle über Ihre Codebasis und Ihren Denkprozess haben, sollten Sie das auf jeden Fall tun.
  • Wenn nicht, werden Sie dies an dem Tag bereuen, an dem eine neue Person eine BItem-Ableitung einführt, die Sie nicht kommen sahen votre BList oder BDictionary.

Wenn Sie dies tun müssen, verpacken Sie es zumindest (Kontrolle des Zugriffs auf die Liste) in einer Klasse, die stark typisierte Methodensignaturen hat.

BString GetString(BInteger);
SetString(BInteger, BString);

BStrings akzeptieren und zurückgeben, auch wenn Sie sie intern in einer BList of BItems speichern. (lassen Sie mich teilen, bevor ich meine 2 B oder nicht 2 B mache)

0voto

John Christensen Punkte 4940

Hm. Ich würde sogar behaupten, dass die erste Zeile des Codes besser lesbar ist als die zweite - es dauert etwas länger, um herauszufinden, was da vor sich geht, aber es ist offensichtlicher, dass Sie Objekte als BList oder BDictionary behandeln. Die Anwendung der Methoden auf die abstrakte Klasse verbirgt dieses Detail, was es schwieriger machen kann, herauszufinden, was Ihre Methode tatsächlich tut.

0voto

Wenn Sie Generika einführen, können Sie das Gießen vermeiden.

class DecodedTorrent : BDictionary<BDictionary<BList<BDictionary<BInteger>>>>
{
}

DecodedTorrent torrent = BItem.DecodeFile("mytorrent");
int x = torrent["info"]["files"][0]["length"];

Hmm, aber das wird wahrscheinlich nicht funktionieren, da die Typen von dem Weg abhängen können, den man durch die Struktur nimmt.

0voto

Geht es nur mir so

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

Das BDictionary cast 'torrent' wird nicht benötigt, da es als BDictionary deklariert ist.

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

Diese führen nicht zum gewünschten Ergebnis, da der Rückgabetyp immer noch die abstrate Version ist, so dass man immer noch casten muss.

Der umgeschriebene Code müsste sein

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

Was genauso schlimm ist wie das erste Los

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