11 Stimmen

Warum nicht immer Generics verwenden?

Ich lese bestehende Beiträge zu Generika bei SO. Wenn Generika so viele Vorteile wie Typsicherheit, keine Umwandlungs-Overhead und Schnelligkeit haben, warum sollte man es dann nicht immer verwenden? Warum würdest du jemals ein nicht-generisches Objekt verwenden?

Bearbeitet (Frage weiter unten erweitert)

Ich bin ein wenig verwirrt. Das letzte Mal, als ich über Generika gelesen habe, vor ein paar Monaten, las ich, dass Generika verwendet werden sollten, wenn der Typ in den Parametern variabel ist, um Fehler zu vermeiden. Was ich jetzt zu lesen scheine, ist jedoch, dass Generika die Implementierung auf einen festen Typ beschränken, während ein nicht-generisches Objekt es Ihnen ermöglicht, Parameter-Typen zur Laufzeit zu definieren.

Bitte helfen Sie mir zu sehen, was ich übersehe?

Zweitens sind solche Konstrukte in richtigen OOP-Designs (Generika usw.) hilfreich, wenn Sie in einem Team arbeiten und Ihr Code gemeinsam genutzt wird. Für einen einzelnen Programmierer im kleinen Maßstab, der weiß, welcher Typ im Parameter kommen muss, scheint es keinen Grund zur Sorge zu geben und nur wenig Unterschied zwischen der Verwendung eines generischen oder nicht generischen Typs. Ist das korrekt?

0 Stimmen

Neu bearbeiten: "Ein nicht generisches Ding ermöglicht es, den Parameter typ zur Laufzeit zu definieren." - nein, es gibt keinen Parameter zu definieren. Wenn überhaupt, ist dieser Satz rückwärts; ein nicht-generisches aber getyptes Argument beschränkt dich auf einen Typ, während ein generisches Argument dem Aufrufer die Auswahl lässt - also bekommen Generics den Vorteil. Ansonsten bist du hauptsächlich auf object beschränkt.

0 Stimmen

Bezüglich Ihres Punktes zum Team gegen Einzelperson: Ich sehe einfach keinen Grund, der irgendetwas anders macht; es sei denn, Sie würden absichtlich einen Weg gehen, um Ihre Methoden weniger klar zu machen? Wenn ja, könnten Sie sie alle auch umbenennen in void A(object a, object b, object c). Der bessere Weg, es zu betrachten, ist einfach: gilt diese Methode für einen spezifischen Typ, oder kann die gleiche Logik für einen beliebigen Typ verwendet werden; wenn letzteres, sind Generika eine gute Wahl.

11voto

Marc Gravell Punkte 970173

Im Allgemeinen sollten Sie - es gibt jedoch Zeiten (besonders beim Schreiben von Bibliothekscodes), in denen es nicht möglich ist (oder sicherlich nicht bequem), den Aufrufstyp zu kennen (auch in Bezug auf T), sodass nicht generische Typen (hauptsächlich Schnittstellen wie IList, IEnumerable) sehr nützlich sind. Datenbindung ist ein gutes Beispiel dafür. Tatsächlich wird alles, was Reflexion beinhaltet, durch die Verwendung von Generika im Allgemeinen viel schwieriger gemacht, und da Sie durch die Natur der Reflexion bereits _diese Vorteile verloren haben, könnten Sie genauso gut zu nicht generischem Code wechseln. Reflexion und Generika sind überhaupt nicht sehr gute Freunde._

1 Stimmen

@Mark Können Sie ein Beispiel liefern?

0 Stimmen

@Tymek Ich habe bereits Reflexion und Datenbindung erwähnt. Kannst du genauer sagen, wonach du suchst?

1 Stimmen

@MarcGravell Ja, tut mir leid. Könnten Sie ein Beispiel geben, wann es nicht praktisch ist, über den Aufrufstyp auch in Bezug auf T Bescheid zu wissen?

4voto

Pontus Gagge Punkte 16933

Generics können schnell und typsicher sein, aber sie fügen auch Komplexität hinzu (eine weitere Dimension, die sich ändern kann und von Programmierern verstanden werden muss). Wer wird Ihren Code pflegen? Nicht jeder Trick mit Generics (oder Lambdas, oder Dependency Injection, oder ...) ist es wert. Denken Sie darüber nach, welches Problem Sie lösen möchten und welche Teile dieses Problems sich in Zukunft ändern könnten. Entwerfen Sie für diese Fälle. Die optimal flexibelste Software ist zu komplex, um von sterblichen Programmierern gewartet zu werden.

2 Stimmen

Einige gültige allgemeine Punkte, aber ich bin mir nicht sicher, ob ich sehen kann, wie Generika hier viel Komplexität hinzufügt... tatsächlich ist es in vielerlei Hinsicht schwieriger, mit Generika etwas falsch zu machen (der Compiler wird sich beschweren).

2 Stimmen

Marc, dein Kontext ist nicht genau kristallklar. Das Hinzufügen eines Designelements bringt immer Komplexität mit sich. Eine weitere Schicht der Indirektion fügt an sich nicht viel Komplexität hinzu, aber alles summiert sich. Die Frage ist, ob zusätzliche Elemente es wert sind. Generica falsch zu benutzen, während du Code schreibst, ist eine Sache: Code zu verstehen, den du pflegen musst, ist eine ganz andere. Wenn du eine Business-Schicht Order-Klasse hast, in welchen Fällen würdest du sie generisch machen? Für welche Parameter? Die Antwort lautet leider: es kommt darauf an!

2 Stimmen

+1 Ausgezeichnete Antwort. Eine der wichtigsten Überlegungen beim Codieren ist, ob die Vorteile eines bestimmten Designs die Kosten der zusätzlichen Komplexität überwiegen.

2voto

Mania Punkte 1698

Manchmal sind "Objekt"-Sammlungen einfach unvermeidlich. Oft passiert das, wenn es mehrere Typen im selben Steuerelement/Sammlung gibt - der einzige Typ, den sie gemeinsam haben, ist "Objekt", und deshalb ist das der beste Typ für Ihre Sammlung.

Ein weiterer Fall für Objekt (nicht sammlungsbezogen), der von Zeit zu Zeit auftaucht, ist bei der PropertyGrid zu sehen. Ein PropertyGrid eines Drittanbieters kann es Ihnen ermöglichen, einen "Validator" anzuhängen, der zurückgibt, ob der neue Wert des Benutzers für eine bestimmte Eigenschaft im Raster akzeptabel ist. Da das PropertyGrid nicht weiß, welche Eigenschaften es anzeigen wird, kann es dem Validator nur ein Objekt geben - obwohl der Validator genau weiß, mit welchem Typ er aufgerufen wird.

Aber wie Mark es erklärt - die meisten (alle?) der nicht generischen Sammlungen in .NET sind nur aus historischen Gründen vorhanden. Wenn .NET heute neu gemacht werden würde, können Sie sicher sein, dass die Standardbibliothek sehr anders aussehen würde.

0 Stimmen

@Mark: Mein langjähriger Zweifel wurde beseitigt. Es ist gut zu wissen, dass nicht-generische Dinge nur aus Gründen der Vererbung vorhanden sind.

1 Stimmen

@RPK: Tatsächlich gibt es noch einige andere Vorteile bei nicht generischen Dingen. Zum Beispiel, wenn eine Klasse SuperCollection IList implementiert aber auch eine schreibgeschützte nicht generische ICollection implementiert, und eine SuperCollection an eine Routine übergeben wird, die ein IEnumerable akzeptiert und die Count-Erweiterungsmethode aufruft, dann kann diese Count-Methode ICollection.Count verwenden, ohne die Elemente in der Liste aufzählen zu müssen.

0voto

nawfal Punkte 65966

Generics sind im Allgemeinen der richtige Weg. Die gesteigerte Komplexität könnte ein Argument dagegen sein, aber meiner Meinung nach lohnt es sich absolut, die Vorteile in den meisten Fällen in Betracht zu ziehen. Als Faustregel, umarme es. Reflection ist ein weiterer Fall, bei dem Generics es schwieriger machen. Aber ich vermeide Generics nur, wenn es keinen Sinn ergibt.

Das eine Szenario, in dem ich Generics vermeide, ist, wenn eine Methode generisch erstellt wird, wenn sie keinen beabsichtigten Zweck von Generics erfüllt und irreführend sein kann. Generics wurden für eine bessere Typsicherheit und Performance (bei Werttypen) eingeführt. Wenn es weder das eine noch das andere erfüllt, kann ein generischer Ansatz den Entwickler in die Irre führen. Zum Beispiel betrachten Sie den Ausschnitt aus dieser Frage:

public void DoSomething(T request)
{
    if (request == null) 
        throw new ArgumentNullException("request");

    if (request is ISomeInterface)
        DoSomething();
    else
        DoSomethingElse();
}

In der obigen Methode wird nichts generisch behandelt. Es wird noch schlimmer, wenn Sie in einer generischen Methode viele Typüberprüfungen durchführen:

public void DoSomething(T request)
{
    if (typeof(T) == typeof(X))
        DoSomething();
    else if (typeof(T) == typeof(Y))
        DoSomethingElse();
    ...
}

was dem Sinn von Generics komplett widerspricht. Es gibt bessere Muster, um Typüberprüfungen zu vermeiden, aber wenn es unbedingt erforderlich ist, dann bevorzuge ich die Annahme von object als Parameter. Ein weiterer ähnlicher Fall ist, wenn Werttypen sowieso in einer generischen Methode geboxt werden:

public void DoSomething(T request)
{
    var type = request.GetType(); //schon geboxt im Fall von Werttypen.

    //oder andere Reflektionsaufrufe, die eine Boxung beinhalten
}

Anhand der Methodendefinition ist man versucht zu denken, dass diese Methode keine Boxungsstrafen hat, aber es dem Aufrufer deutlich zu machen, dass es eine gute Möglichkeit ist. Ich mag es so:

public void DoSomething(object request) //boxt hier
{
    var type = request.GetType(); 

    //oder andere Reflektionsaufrufe, die eine Boxung beinhalten
}

Abgesehen davon, bevorzuge ich Generics die ganze Zeit.


Ein Randfall, in dem sich die beiden unterschiedlich verhalten könnten, ist bei Verwendung von dynamic.

public void Generic(T request)
{
}

public void Object(object request)
{
}

dynamic d = null;
Object(d); //keine Probleme
Generic(d); //Laufzeitexplosion; Typargument kann nicht abgeleitet werden

Nicht von Bedeutung, nur eine alberne Verhaltensdifferenz, die es wert ist, im Hinterkopf zu behalten.

0voto

GPopat Punkte 357

Generics macht den Code viel wiederverwendbarer, fügt Typsicherheit usw. hinzu. Wenn Sie eine gute API (Jdk, Scala usw.) sehen, werden Sie überall Generics sehen. Die größte Herausforderung bei Generics besteht darin, dass es ein wenig schwierig ist, sich daran zu gewöhnen, und daher finden viele Programmierer Generics nicht lesbar.

Aber hier sollten wir das eigentliche Problem lösen (Wissenslücke schließen), anstatt Generics zu vermeiden.

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