Ich weiß nicht warum, aber ich habe immer das Gefühl, zu "schummeln", wenn ich Reflektion benutze - vielleicht liegt es an dem Performance-Hit, den ich kenne.
Ein Teil von mir sagt, wenn es Teil der Sprache ist, die Sie verwenden, und es das erreichen kann, was Sie versuchen, dann warum nicht. Der andere Teil von mir sagt, es muss einen Weg geben, wie ich das ohne Reflektion machen kann. Ich denke, vielleicht hängt es von der Situation ab.
Auf welche potenziellen Probleme muss ich achten, wenn ich Reflexion verwende, und wie sollte ich mir darüber Gedanken machen? Wie viel Aufwand lohnt es sich, nach einer konventionelleren Lösung zu suchen?
Antworten:
Nein, es ist kein Betrug - es ist eine Möglichkeit, Probleme in einigen Programmiersprachen zu lösen.
Jetzt ist es oft nicht die beste (sauberste, einfachste, am einfachsten zu wartende) Lösung. Wenn es einen besseren Weg gibt, benutze diesen in der Tat. Manchmal ist dies jedoch nicht der Fall. Oder wenn ja, ist es sehr viel komplexer und erfordert viel Code-Duplikation usw., was es unmöglich macht (auf lange Sicht schwierig zu warten).
Zwei Beispiele aus unserem aktuellen Projekt (Java):
fieldX
mit dem entsprechenden Feld in der Klasse abzugleichen und letzteres zu initialisieren. In einigen Fällen kann ein einfaches GUI-Dialogfeld direkt aus den identifizierten Eigenschaften erstellt werden. Ohne Reflexion würde dies Hunderte von Codezeilen in mehreren Anwendungen erfordern. Durch Reflexion konnten wir schnell und ohne großen Aufwand ein einfaches Tool zusammenstellen und uns auf den wichtigen Teil konzentrieren (Regressionstest unserer Web-App, Analyse von Serverprotokollen usw.), anstatt auf den irrelevanten Teil.Die Quintessenz ist, wie jedes mächtige Werkzeug, Reflexion kann auch verwendet werden, um sich in den Fuß zu schießen. Wenn Sie lernen, wann und wie Sie es (nicht) verwenden, können Sie elegante und saubere Lösungen für ansonsten schwierige Probleme finden. Wenn Sie es missbrauchen, können Sie ein ansonsten einfaches Problem in ein komplexes und hässliches Chaos verwandeln.
quelle
if (propName = n) setN(propValue);
, können Sie Ihre (dh) XML-Tags genauso benennen wie Ihre Code-Eigenschaften und eine Schleife über sie ausführen. Diese Methode vereinfacht auch das spätere Hinzufügen von Eigenschaften.Es betrügt nicht. Aber es ist normalerweise eine schlechte Idee im Produktionscode, zumindest aus den folgenden Gründen:
Ich würde vorschlagen, die Verwendung der Reflexion auf die folgenden Fälle zu beschränken:
In allen anderen Fällen würde ich vorschlagen, einen Ansatz zu finden, der Reflexionen vermeidet. Das Definieren einer Schnittstelle mit den entsprechenden Methoden und das Implementieren dieser Schnittstelle in der Gruppe von Klassen, für die Sie die Methode (n) aufrufen möchten, ist normalerweise ausreichend, um die einfachsten Fälle zu lösen.
quelle
Reflexion ist nur eine andere Form der Metaprogrammierung und genauso gültig wie die typbasierten Parameter, die Sie heutzutage in den meisten Sprachen sehen. Reflexion ist leistungsfähig und allgemein, und Reflexionsprogramme haben eine hohe Wartbarkeitsstufe (natürlich bei korrekter Verwendung) und sind mehr als rein objektorientierte oder prozedurale Programme. Ja, Sie zahlen einen Leistungspreis, aber ich würde gerne ein langsameres Programm wählen, das in vielen oder sogar den meisten Fällen besser gewartet werden kann.
quelle
Sicher hängt alles davon ab, was Sie erreichen wollen.
Ich habe zum Beispiel eine Anwendung zur Medienprüfung geschrieben, die mithilfe der Abhängigkeitsinjektion ermittelt, welche Art von Medien (MP3- oder JPEG-Dateien) geprüft werden sollen. Die Shell musste ein Raster mit den relevanten Informationen für jeden Typ anzeigen, wusste jedoch nicht, was angezeigt werden sollte. Dies wird in der Assembly definiert, die diesen Medientyp liest.
Daher musste ich Reflection verwenden, um die Anzahl der anzuzeigenden Spalten sowie ihre Typen und Namen zu ermitteln, damit ich das Raster korrekt einrichten konnte. Es bedeutete auch, dass ich die injizierte Bibliothek aktualisieren (oder eine neue erstellen) konnte, ohne anderen Code oder andere Konfigurationsdateien zu ändern.
Die einzige andere Möglichkeit wäre gewesen, eine Konfigurationsdatei zu haben, die aktualisiert werden müsste, wenn ich den Typ des zu überprüfenden Mediums gewechselt hätte. Dies hätte zu einem weiteren Fehler für die Anwendung geführt.
quelle
Reflection ist ein großartiges Tool, wenn Sie Bibliotheksautor sind und daher keinen Einfluss auf die eingehenden Daten haben. Eine Kombination aus Reflektion und Metaprogrammierung ermöglicht es Ihrer Bibliothek, nahtlos mit beliebigen Aufrufern zusammenzuarbeiten, ohne dass diese durch Rahmen der Codegenerierung usw. springen müssen.
Ich versuche jedoch, von Überlegungen im Anwendungscode abzuraten . Auf der App-Ebene sollten Sie verschiedene Metaphern verwenden - Schnittstellen, Abstraktion, Kapselung usw.
quelle
Reflection eignet sich hervorragend zum Erstellen von Tools für Entwickler.
Dies ermöglicht Ihrer Build-Umgebung, den Code zu überprüfen und möglicherweise die richtigen Tools zum Manipulieren / Initialisieren des Codes zu generieren.
Als allgemeine Programmiertechnik kann sie nützlich sein, ist aber spröder, als sich die meisten Menschen vorstellen.
Eine wirklich entwicklungsbedingte Verwendung für Reflection (IMO) ist, dass das Schreiben einer generischen Streaming-Bibliothek sehr einfach ist (solange sich Ihre Klassenbeschreibung nicht ändert (dann wird es eine sehr spröde Lösung)).
quelle
Die Verwendung von Reflexion ist in OO-Sprachen häufig schädlich, wenn sie nicht mit großem Bewusstsein verwendet wird.
Ich habe die Anzahl der schlechten Fragen, die ich auf StackExchange-Sites gesehen habe, verloren
Hier sind typische Beispiele.
Der meiste Sinn von OO ist das
Wenn zu irgendeinem Zeitpunkt in Ihrem Code Punkt 2 für ein Objekt, das Sie übergeben haben, nicht gültig ist, sind einer oder mehrere davon wahr
Schlecht ausgebildete Entwickler verstehen das einfach nicht und glauben, dass sie irgendetwas in ihrem Code weitergeben können und tun, was sie von einer (hartcodierten) Menge von Möglichkeiten wollen. Diese Idioten benutzen viel Reflexion .
Bei OO-Sprachen sollte eine Reflektion nur in Meta-Aktivitäten (Klassenladeprogramme, Abhängigkeitsinjektion usw.) erforderlich sein. In diesen Kontexten ist eine Reflexion erforderlich, da Sie einen allgemeinen Dienst bereitstellen, der Sie bei der Manipulation / Konfiguration von Code unterstützt, von dem Sie aus einem guten und legitimen Grund nichts wissen. In fast jeder anderen Situation, wenn Sie nach Reflexion greifen, tun Sie etwas Falsches und müssen sich fragen, warum dieser Code nicht genug über das Objekt weiß, das an ihn übergeben wurde.
quelle
In Fällen, in denen die Domäne der reflektierten Klassen genau definiert ist, besteht eine Alternative darin, Reflection zusammen mit anderen Metadaten zu verwenden, um Code zu generieren , anstatt Reflection zur Laufzeit zu verwenden. Ich mache das mit FreeMarker / FMPP; Es gibt viele andere Tools zur Auswahl. Dies hat den Vorteil, dass Sie am Ende "echten" Code haben, der sich leicht debuggen lässt usw.
Abhängig von der Situation kann dies dazu beitragen, dass Sie viel schnelleren Code erstellen - oder nur viel Code aufblähen. Es vermeidet die Nachteile der Reflexion:
zuvor erwähnt.
Wenn das Nachdenken wie Schummeln klingt, kann es sein, dass Sie es auf eine Menge Rätselraten stützen, bei denen Sie sich nicht sicher sind, und Ihr Bauch warnt Sie, dass dies riskant ist. Stellen Sie sicher, dass Sie die Metadaten, die der Reflektion inhärent sind, durch Ihre eigenen Metadaten erweitern können. Dort können Sie alle Macken und Sonderfälle der realistischen Klassen beschreiben, denen Sie möglicherweise begegnen.
quelle
Es ist kein Betrug, aber wie jedes Werkzeug sollte es für das verwendet werden, was es lösen soll. Durch Reflektion können Sie per Definition Code durch Code prüfen und ändern. Wenn Sie dies tun müssen, ist Reflexion das Werkzeug für den Job. Bei der Reflexion dreht sich alles um Meta-Code: Code, der auf Code abzielt (im Gegensatz zu regulärem Code, der auf Daten abzielt).
Ein Beispiel für eine gute Reflection-Verwendung sind generische Web-Service-Schnittstellenklassen: Ein typisches Design besteht darin, die Protokollimplementierung von der Nutzlastfunktionalität zu trennen. Dann haben Sie eine Klasse (nennen wir sie
T
), die Ihre Payload implementiert, und eine andere, die das Protokoll (P
) implementiert .T
ist ziemlich einfach: Schreiben Sie für jeden Anruf, den Sie tätigen möchten, einfach eine Methode, die das tut, was sie tun soll.P
Webdienstaufrufe müssen jedoch Methodenaufrufen zugeordnet werden. Es ist wünschenswert, dieses Mapping generisch zu machen, da es Redundanz vermeidet und inP
hohem Maße wiederverwendbar macht . Reflection bietet die Möglichkeit, KlassenT
zur Laufzeit zu untersuchen und ihre Methoden auf der Grundlage von Zeichenfolgen aufzurufen, dieP
über das Webdienstprotokoll übergeben werden, ohne dass während der Kompilierung Kenntnisse über Klassen erforderlich sindT
. Mit der Regel 'Code über Code' kann man argumentieren, dass die KlasseP
den Code in der KlasseT
als Teil ihrer Daten hat.Jedoch.
Reflection bietet Ihnen auch Tools, mit denen Sie Einschränkungen des Typsystems der Sprache umgehen können. Theoretisch können Sie alle Parameter als Typ übergeben
object
und ihre Methoden über Reflections aufrufen. Voilà, die Sprache, die eine starke statische Typisierungsdisziplin durchsetzen soll, verhält sich jetzt wie eine dynamisch typisierte Sprache mit später Bindung, nur dass die Syntax weitaus ausgefeilter ist. Jede einzelne Instanz eines solchen Musters, die ich bisher gesehen habe, war ein schmutziger Hack, und ausnahmslos wäre eine Lösung innerhalb des Typensystems der Sprache möglich gewesen, und sie wäre in jeder Hinsicht sicherer, eleganter und effizienter gewesen .Es gibt einige Ausnahmen, z. B. GUI-Steuerelemente, die an verschiedene nicht verwandte Arten von Datenquellen gebunden werden können. Es ist nicht realistisch, dass Ihre Daten eine bestimmte Schnittstelle implementieren, damit Sie sie binden können, und der Programmierer muss auch keinen Adapter für jeden Datenquellentyp implementieren. In diesem Fall ist es sinnvoller, die Art der Datenquelle durch Reflektion zu ermitteln und die Datenbindung anzupassen.
quelle
Ein Problem, das wir bei Reflection hatten, ist das Hinzufügen von Obfuscation zum Mix. Alle Klassen erhalten neue Namen und das plötzliche Laden einer Klasse oder Funktion anhand ihres Namens funktioniert nicht mehr.
quelle
Es kommt ganz darauf an. Ein Beispiel für etwas, das ohne Reflektion nur schwer möglich wäre, wäre die Replikation von ObjectListView . Es generiert auch IL-Code im laufenden Betrieb.
quelle
Reflexion ist die primäre Methode zur Erstellung von konventionellen Systemen. Es würde mich nicht überraschen, dass es in den meisten MVC-Frameworks häufig verwendet wird. Es ist eine wichtige Komponente in ORMs. Möglicherweise verwenden Sie bereits Komponenten, die täglich damit erstellt werden.
Die Alternative für eine solche Verwendung ist die Konfiguration, die ihre eigenen Nachteile hat.
quelle
Reflexion kann Dinge bewirken, die praktisch anders nicht möglich sind.
Überlegen Sie sich beispielsweise, wie Sie diesen Code optimieren können:
Es gibt einen teuren Testfehler in der Mitte der inneren Schleife, aber zum Extrahieren muss die Schleife für jeden Operator einmal neu geschrieben werden. Reflexion ermöglicht es uns, die Leistung mit dem Extrahieren dieses Tests gleichzusetzen, ohne die Schleife ein Dutzend Mal zu wiederholen (und dadurch die Wartbarkeit zu beeinträchtigen). Generieren und kompilieren Sie einfach die Schleife, die Sie im laufenden Betrieb benötigen.
Ich habe diese Optimierung tatsächlich durchgeführt , obwohl die Situation etwas komplizierter war und die Ergebnisse erstaunlich waren. Eine Verbesserung der Leistung um eine Größenordnung und weniger Codezeilen.
(Hinweis: Ich habe anfangs versucht, ein Func anstelle eines Char zu übergeben, und es war etwas besser, aber nicht annähernd das 10-fache der erreichten Reflektion.)
quelle
Auf keinen Fall wird betrogen ... Vielmehr können Anwendungsserver die vom Benutzer erstellten Klassen mit dem Namen ihrer Wahl ausführen, wodurch dem Benutzer Flexibilität geboten wird, ohne sie zu betrügen.
Und wenn Sie den Code einer .class-Datei (in Java) sehen möchten, stehen Ihnen mehrere Dekompilierer kostenlos zur Verfügung!
quelle