Dieser Artikel wurde maschinell übersetzt.

Programmiererpraxis

Multiparadigmatisches .NET, Teil 6: Reflektive Metaprogrammierung

Ted Neward

Der Marathon wird fortgesetzt. In meinem letzten Ausgabe Automatische metaprogramming besprochen, und von diesem Punkt die Ideen der Gemeinsamkeiten und Variabilität auf ein Gefühl vertraut machen sollten. Ich bin jetzt Full-on sprechen metaprogramming – die Idee der Programme, die Programme zu schreiben.

Last month I examined one of the more familiar approaches to metaprogramming: automatic metaprogramming, more commonly known as code generation. Bei einem automatischen metaprogramming schreiben Entwickler Programme, mit denen die "Things" generiert werden. Dies erfolgt in der Regel mit Hilfe von Befehlszeilenparametern oder anderen Produktionsfaktoren wie relationale Datenbankschemas, XSD-Dateien oder sogar WSDL (Web Services Description Language) für Dokumente.

Da Codegenerierung im Wesentlichen ist "als ob" der Code wurden geschrieben durch menschliche Hände Variabilität zu einem beliebigen Zeitpunkt innerhalb des Codes kommen kann. Datentypen, Methoden, Vererbung... die gesamte kann der Arbeitsprozess nach Bedarf an. Der Nachteil ist natürlich, verfolgt. Erstens kann zu viel Variabilität Rendern des generierten Codes (und mehr häufig als nicht die Vorlagen, die durch die Code generiert wird) schwierig zu verstehen. Zweitens können generierte Artefakte im wesentlichen nicht bearbeitet werden, es sei denn, die Codegenerierung irgendwie Weg durch die Verwendung von partiellen Klassen partitioniert ist oder Codegenerierung nicht mehr erforderlich ist.

Glücklicherweise c# und Visual Basic bieten weitere metaprogrammatic Taktiken als nur automatische metaprogramming und getan haben seit den jüngsten Tagen von Microsoft.NET Framework.

Persistente Probleme

Ein wiederkehrendes Problem in der objektorientierten Umgebung ist der Objekt-relationales Dauerhaftigkeit – auch bekannt als Objekt-XML-Konvertierung oder in moderneren Welt von Web 2.0, JSON-Object-Transformation. Trotz besten Bemühungen für Entwickler scheint es unvermeidlich, dass Objektmodelle irgendwie die CLR escape müssen und über das Netzwerk verschieben oder auf Datenträger zu verschieben und wieder zurück. Und hierin liegt das Problem: die vorherigen Modes of Design – Verfahrens- und objektorientierte – nicht bieten gute Lösungen für dieses Dilemma.

Betrachten Sie eine kanonische vereinfachte Darstellung im Code modelliert einem Menschen erstellt wurden:

class Person {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public int Age { get; set; }
  public Person(string fn, string ln, int a) {
    FirstName = fn; LastName = ln; Age = a;
  }
}

Beibehalten von Instanzen dieses Objekts, wie sich herausstellt, ist nicht schwierig, besonders, da die Eigenschaften (in ihrer einfachsten Form) zu eins mit den Spalten einer relationalen Tabelle entsprechen, und die Eigenschaften öffentlich zugänglich sind. Sie können eine Prozedur, um Instanzen der Person, extrahieren die Datenbits, fügen diese Bits in SQL-Anweisungen, und die resultierende Anweisung an die Datenbank senden schreiben:

class DB {
  public static bool Insert(Person p) {
    // Obtain connection (not shown)
    // Construct SQL
    string SQL = "INSERT INTO person VALUES (" +
      "'" + p.FirstName + "', " +
      "'" + p.LastName + "', " +
      p.Age + ")";
    // Send resulting SQL to database (not shown)
    // Return success or fail
    return true;
  }
}

Der Nachteil einer prozeduralen Ansatz rears den hässlichen Kopf ziemlich schnell: neue Typen (Pet, Dozenten, Studenten usw.), die auch eingefügt werden soll werden neue Methoden der überwiegend ähnlichen Code erfordern. Schlimmer noch, wenn an die öffentliche API verfügbar gemachten Eigenschaften nicht eins zu eins mit internen Felder oder Spalten übereinstimmen, kompliziert schnell – Entwickler Schreiben von SQL-Routinen müssen wissen, welche Felder beibehalten benötigen und die, einen ziemlich klaren Verstoß gegen die Kapselung nicht.

Vom Entwurfsstandpunkt objektrelationale Problem möchte beibehalten von Daten in die Einheitlichkeit der SQL-Esque Teile erfassen, so dass an einem zentralen Ort Verwalten von Datenbankverbindungen und Transaktionen behandelt wird aber weiterhin ermöglicht Variabilität in die tatsächliche Struktur beibehalten wird unter anderem (oder abgerufen).

Wie bereits erwähnt, aus unserem früheren Untersuchungen, prozedurale Ansätze algorithmische Gemeinsamkeit erfassen und die Vererbung strukturelle Kompatibilität erfasst (positiv) Variabilität gewährleistet und gleichzeitig – aber keines dieser genau tun was benötigt wird. Die Vererbung Ansatz – eine Basisklasse die Gemeinsamkeiten bei Inbetriebnahme – erfordern, dass Entwickler arbeiten in abgeleiteten Klassen die SQL-Zeichenfolge angeben und ein großer Teil der i/o-Buchhaltung (im wesentlichen Stiftspitze Gemeinsamkeit wieder in abgeleiteten Klassen) behandelt wird. Prozedurale Ansatz muss eine Art von Variabilität innerhalb der Prozedur (Extrahieren, und erstellen die auszuführende SQL-Anweisung) angegebenen von außerhalb der Prozedur, die erweist sich als relativ schwer zu erreichen.

Geben Sie Metaprogramming

Eine Lösung für das Problem der objektrelationalen Persistenz häufig zitiert wird, automatisch metaprogramming: mit das Datenbankschema erstellen Sie Klassen, die wissen, wie beibehalten werden selbst in und aus der Datenbank.

Leider hat dies alle herkömmlichen Probleme der Codegenerierung, besonders wenn Klassen ändern möchten die Objektdarstellung ist etwas einfacher, mit zu arbeiten, als was impliziert, dass die physischen Datenbankschemas. Beispielsweise wäre eine VARCHAR(2000) Spalte viel einfacher zu bearbeiten, wenn es sich um ein.NET Framework System.String und keine Char [2000].

Andere Techniken für die Generierung von Code aus der Klassendefinitionen gestartet und erstellt ein Datenbankschema zusammen mit der permanenten Klassendefinitionen … aber das bedeutete, dass irgendwie jetzt die Objekthierarchie in zwei verschiedenen Modellen, ausschließlich für Dauerhaftigkeit und eine für alles andere dupliziert wurden. (Beachten Sie, dass, sobald das Objekt in XML transformiert werden muß, eine weitere Hierarchie in springs, dass Sie behandeln müssen, und einen weiteren eine JSON zu behandeln. Schnell wächst dieses Ansatzes hartnäckigsten.)

Glücklicherweise Reflektierend metaprogramming bietet potenziellen Befreiung. Ein Teil der.NET Framework seit 1.0 System.Reflection ermöglicht Entwicklern, untersuchen Sie die Struktur von Objekten zur Laufzeit, die in diesem Fall der Persistenz-orientierte Infrastruktur die Möglichkeit, untersuchen Sie die Struktur des Objekts beibehalten wird ermöglicht, und von dort aus erforderliche SQL generieren. Eine grundlegende Einführung in System.Reflection gut dokumentiert ist sowohl in der MSDN-Dokumentation unter MSDN.Microsoft.com/library/f7ykdhsy(v=VS.400) und in der MSDN Magazin Artikel "Verwendung von Reflektion zu ermitteln und bewerten die am häufigsten verwendeten Typen in der.NET Framework")MSDN.Microsoft.com/magazine/cc188926) und "Tiefe Einblicke in CLR: Reflexionen über Reflektion"und "Tiefe Einblicke in CLR: Reflexionen über Reflektion" (MSDN.Microsoft.com/magazine/cc163408). Es wird keinem weiteren hier erläuterten.

Reflektion ermöglicht Angleichung der Algorithmus, wobei Variabilität der Struktur, die durch dieses Algorithmus geändert werden, alle, während Sie den Vorgang fortsetzen, um das Erscheinungsbild der Kapselung erhalten. Da Reflektion (in einer entsprechend konfigurierten Sicherheitskontext) Zugriff auf Private Member von Objekten hat, können interne Daten noch bearbeitet werden, ohne Erzwingen eines dieser Datenmember auf öffentlich gemacht werden. Positive Werte zur Variabilität – die Möglichkeit, durch Hinzufügen von Elementen zu variieren – ist, wie immer, leicht zu arbeiten, als die Anzahl der Felder für die meisten Reflection basierender Code weitgehend irrelevant ist. Negative Werte zur Variabilität der – die Möglichkeit, variieren, indem Sie die Dinge – scheint jedoch überhaupt zu passen. Nachdem alle muss keine Klasse ohne Felder wirklich beibehalten werden, ist es? Und eine Reflektion-basierten Infrastruktur, die private Felder durchlaufen muss nicht auf allen als unsinnig wie, mag nicht Schleifen Problem dar.

Negative Variabilität hier ist jedoch etwas anders als einfach nicht mit Feldern. In bestimmten Szenarios wird die Person-Klasse internen Felder haben, die nicht auf allen beibehalten werden sollen. Oder mehr abrupt die Person-Klasse Felder haben möchte, dauerhaften in ein anderes Datenformat als die CLR-gehosteten Darstellung. Person.BirthDate möchte vielleicht oder sogar über drei Spalten (Tag, Monat, Jahr) anstatt in einer einzelnen Spalte als Zeichenfolge gespeichert werden. Mit anderen Worten: negative Variabilität in einer reflektierenden metaprogrammatic sinnvoll ist, nicht das Fehlen von Feldern, sondern etwas anders auf bestimmte Instanzen von Typen, die ansonsten in eine Standardmethode (Beibehaltung einer Zeichenfolge zu einer VARCHAR-Spalte wird der Standard behandelt wirdzum Beispiel, aber für einen oder mehrere bestimmte Felder, eine Zeichenfolge, die eine BLOB-Spalte beibehalten).

Die.NET Framework verwenden benutzerdefinierte Attribute, um diese negative Variabilität zu vermitteln. Entwickler verwenden Attribute auf Tagelemente innerhalb der Klasse der Wunsch nach, die die benutzerdefinierten Behandlung, wie z. B. @ NotSerialized bei der Objektserialisierung des zu vermitteln. Es ist wichtig, die allerdings beachten, dass das Attribut selbst nichts ist – es ist lediglich ein Flag, das den Code für dieses Attribut suchen. Von sich selbst dann das Attribut bietet keine negativen Werte zur Variabilität, aber lediglich macht es einfacher, kennzeichnen, wenn diese negativen Variabilität in Kick sollte.

Attribute können auch verwendet werden, um positive Variabilität zu vermitteln. Ein Beispiel ist wie die.NET Framework verwendet Attribute vermitteln transaktionale Verarbeitung, unter der Voraussetzung, dass der Mangel an ein Attribut in einer Methode keine transaktionale Affinität jeglicher Art angibt.

Spieglein, Spieglein an der Wand

Ohne Attribute, reflektierenden metaprogramming richtet eine völlig neue Art der Variabilität. Jetzt Namen zum Verweisen auf Elemente innerhalb des Programms (statt über Compiler Symbole) verwendet werden können – und zu einem viel späteren Zeitpunkt (Common Language Runtime) als bisher der Compiler ermöglicht. Beispielsweise verwendet früheste Tropfen der NUnit-Komponententestframework, wie dessen Cousin JUnit in der Java-Platz Reflektion Methoden ermitteln, die begann mit "Test" als Teil des Namens, und davon ausgegangen, dass sie Testmethoden als Teil einer Reihe von Tests ausgeführt wurden.

Der Name basierenden Ansatz erfordert Entwicklern die Elemente, die traditionell für das menschliche Auge reserviert – die Namen der Dinge – und festlegen, dass diese strikte Konventionen, z. B. das Präfix "Test" für NUnit Methoden halten. Die Verwendung von benutzerdefinierten Attributen lockert Vollstreckungsübereinkommens naming-basierte (auf Kosten der jetzt erfordern zusätzliche Codekonstrukte, die in den betreffenden Klassen), im Wesentlichen einen Opt-in-Mechanismus, die Entwickler erstellen muss zustimmen, um die Vorteile der Metaprogram zu erhalten.

Attribute bieten auch die Möglichkeit, Tags beliebiger Daten zusammen mit dem Attribut, das eine weitaus abgestimmte Parametrisierung metaprogrammatic Verhalten bereitstellen. Dies ist etwas in der Regel nicht möglich mit automatischer metaprogramming, insbesondere nicht bei der Client unterschiedliches Verhalten für strukturell ähnlicher möchte (z. B. das strings-to-BLOBs-instead-of-VARCHAR-columns Beispiel aus früher) erstellt.

Aufgrund seiner Natur Runtime gebundene erzwingt Reflektion häufig eine Beeinträchtigung der Code, der häufig verwendet wird. Reflektion bietet darüber hinaus keine Lösungen für die Probleme, die in der automatischen metaprogramming Szenario vom letzten Monat zitiert – die Verbreitung von Klassen, z. B.. Eine andere metaprogrammatic Lösung ist verfügbar, aber für den nächsten Monat warten müssen.

Viel Spaß beim Programmieren!

Ted Neward ist ein Geschäftsführer von Neward & Associates, einer unabhängigen Firma, die sich auf .NET Framework- und Java-Plattformsysteme für Unternehmen spezialisiert hat. Er hat mehr als 100 Artikel geschrieben, ist ein C#-MVP und INETA-Sprecher und hat verfasst und Mitverfasser von einem Dutzend Bücher, einschließlich "Professionelle F#-2.0" (Wrox, 2010). Er berät auch regelmäßig mentors. Erreichen Sie ihn unter ted@tedneward.com Bei Fragen oder consulting-Anforderungen und lesen Sie seinen Blog unter Blogs.tedneward.com.

Dank an den folgenden technischen Experten für die Überprüfung dieses Artikels: Anthony Green