Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Reflection – Fluch oder Segen?

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen.

Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Dezember 2009, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Reflection – Fluch oder Segen?

So wohl Golo wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Golos Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

 

Reflection?

Reflection, Reflexion bzw. Introspektion macht es möglich, Programme wie Daten zu behandeln und so Informationen über Typen / Member abzurufen und auch zu setzen.

Eine Einführung zu Reflection findet sich bspw. hier, und auf myCSharp.de findet sich ein super ausführlicher und tiefgehender Artikel, der auf jeden Fall lesenswert ist.
Darüber hinaus ist es per Reflection möglich, eine späte Bindung in jeglicher Hinsicht zu erreichen. Sei dies das dynamische Laden einer Assembly oder auch das dynamische Aufrufen einer Methode zur Laufzeit.
Damit lassen sich Service Locator und Dependency Injection Container - also Systeme zur Entkoppelung - umsetzen, die je nach Konfiguration eine andere Assembly mitsamt ihren Typen lädt.

Ein praktischer Anwendungsfall wäre ein Serialisierungsmechanismus, der Objekthierarchien bspw. in XML-Form abspeichern kann.
Mithilfe von Reflection können die zu speichernden Typen in Form einer Instanz von System.Reflection.Type abgefragt werden.

Ein Type Objekt macht alles verfügbar, was zu einem Typen gehört, seien dies Methoden, Attribute, Eigenschaften oder gar Felder.

Kleines Beispiel:

int count = 2012;
Type typeOfCount = count.GetType(); // Oder: typeof(int)
Console.WriteLine(typeOfCount);

Ausgabe ist die Stringrepräsentation des Types der mit dem Alias int verknüpft ist, also: “System.Int32”.

Schluss ist damit noch lange nicht, es ist genau so möglich Code zur Kompile- oder Laufzeit dynamisch zu erstellen.
Das .NET Framework bietet die hierzu nötigen Klassen im Namensraum System.Reflection.Emit bereit.

Die Anwendung dieser API ist jedoch relativ umständlich und komplex, dafür extrem mächtig.

 

Segen?

Reflection und Bibliotheken die darauf aufbauen birgen ein riesen Potential und ungeahnte Möglichkeiten.
Die Anwendung ist extrem einfach und viele Features im .NET Framework selber bauen auch auf Reflection auf.

Also auf jeden Fall ein Segen… auf der einen Seite.

 

Fluch?

Auf der anderen Seite kann es auch zu einem Fluch werden, denn mit Reflection kann viel Humbug getrieben werden.
Zum einen ist die Ausführung von Code der Reflection nutzt, um einiges langsamer, was zu Performanceproblemen führen kann, zum anderen kann so auch die Mauer der Zugriffsmodifizierer umgangen werden, da es ohne viel Aufwand möglich ist, auch private Members einer Klasse auszulesen.

Zudem können Probleme auftreten, wenn die Umgebung in der eine Anwendung läuft, zuwenig Rechte hat. Das kann dazu führen, das eine Anwendung überhaupt nicht, oder nur eingeschränkt lauffähig ist, in restriktiven Umgebungen.

Über das dynamische Laden von Assemblies kann es zu Sicherheitsproblemen kommen, falls die geladenen Assemblies schädlichen Code enthalten.

Allerdings lassen sich viele der geschilderten Probleme umgehen bzw. beheben.
Caching – dafür gibt es auch schon diverse, fertige APIs, die auch neben Caching noch weitere Verbesserungen für Reflection bringen.

Alle anderen Probleme brauchen eine vorsichtige und sparsame Verwendung von Reflection.
Mein Credo dabei ist: So viel wie nötig, so wenig wie möglich. Denn sehr vieles geht auch ohne Reflection, kann aber mit Reflection nachgebaut werden.

 

System.Linq.Expressions

Mit der Einführung von LINQ und in dessen Zuge auch dem neuen Namensraum System.Linq.Expressions gibt es eine weitere und einfachere Möglichkeit, Code dynamisch zu generieren und vorallem auch zu analysieren.

Zum einen ist es mit LINQ und den Expression Trees möglich, Lambda Ausdrücke, die in eine Methode eingespiesen werden, auseinander zu nehmen und zu analysieren.
In .NET selber wird das bspw. verwendet, um LINQ abfragen zu parsen und diese anschliessend in ein SQL-Statement zu überführen.

Zumindest laut meinem Wissen, ist es mit dem Expressions-API nur möglich, Methoden dynamisch zu generieren und zu kompilieren. Das ist eine Einschränkung, die allerdings für viele Anwendungsfälle nicht weiter tragisch ist, damit lässt einfach schon relativ viel realisieren.

Das Expressions-API nutzt intern auch Reflection und schlussendlich auch die Klassen aus dem Namensraum System.Reflection.Emit.

Beispiele mit dem Expressions-API:

Addieren von zwei Zahlen per statischem Code.
private void AddPlain()
{
  int one = 1;
  int two = 2;

  int result = one + two;
}

Addieren von zwei Zahlen, die Methode wird dynamisch zusammengebaut.
private static void AddWithExpressions()
{
  ConstantExpression numberOneExpression = Expression.Constant(2);
  ConstantExpression numberTwoExpression = Expression.Constant(3);

  BinaryExpression addExpression = Expression.Add(numberOneExpression, numberTwoExpression);

  var lambdaExpression = Expression.Lambda<Func<int>>(addExpression);
  Func<int> methodDelegate = lambdaExpression.Compile();

  int result = methodDelegate();
}


Funktion zum Addieren von zwei Zahlen per statischm Code.
private static int AddFunctionPlain(int left, int right)
{
  return left + right;
}

Funktion zum Addieren von zwei Zahlen dynamische generiert.
private static Func<int, int, int> GenerateAddFunction()
{
  ParameterExpression leftParameter = Expression.Parameter(typeof(int), "left");
  ParameterExpression rightParameter = Expression.Parameter(typeof(int), "right");

  BinaryExpression addExpression = Expression.Add(leftParameter, rightParameter);

  var lambdaExpression = Expression.Lambda<Func<int, int, int>>(addExpression, leftParameter, rightParameter);
  Func<int, int, int> methodDelegate = lambdaExpression.Compile();

  return methodDelegate;
}

Wie man sehen kann, ist die Nutzung von Expressions zum dynamische Generieren von Methoden für Ihre Mächtigkeit immer noch relativ einfach zu nutzen.

 

Fazit

Das war ein kurzer Rundflug durch Reflection und verwandte Gebiete, in dem ich versucht habe, die Vorteile, Möglichkeiten sowie auch Gefahren aufzuzeigen.
Reflection ist genial, aber nur wenn es sinngemäss und mit Vorsicht eingesetzt wird.

Also: Fluch und Segen :)

Veröffentlicht Dienstag, 1. Dezember 2009 09:37 von Peter Bucher

Kommentare

# Twitter Trackbacks for Peter Bucher : Reflection ??? Fluch oder Segen? [aspnetzone.de] on Topsy.com

# re: Reflection – Fluch oder Segen?

+1

Dienstag, 1. Dezember 2009 19:37 by GENiALi

# this oder kein this

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt,

Dienstag, 16. Februar 2010 21:30 by Peter Bucher

# Felder vs Eigenschaften

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt,

Sonntag, 14. März 2010 16:53 by Peter Bucher

# Abstraktion

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt,

Freitag, 9. April 2010 13:22 by Peter Bucher
Anonyme Kommentare sind nicht zugelassen