www.rolandk.de
- Aktuelle Themen zu .Net -
Achtung: Hier handelt es sich um meine alte Seite.
Die aktuelle ist unter folgendem Link erreichbar: www.rolandk.de/wp/
Home Blog Dynamsche Methoden mit ExpressionTrees




















































Dynamsche Methoden mit ExpressionTrees
Sonntag, den 30. Januar 2011 um 11:25 Uhr

Neulich habe ich mich kurz mit dem Thema ExpressionTrees in .Net beschäftigt und bin begeistert von den Möglichkeiten - allerdings wirft sich für mich auch die Frage nach den Einsatzmöglichkeiten auf. Zunächst zu den Möglichkeiten: Unter .Net 3.5 wurden ExpressionTrees als Basis von Linq-Abfragen eingefügt. ExpressionTrees können Programmlogik in einer Objekthierarchie abbilden, die zur Laufzeit kompiliert werden kann.

Beispiele sind:

  • MethodCallExpression-Objekt für den Aufruf einer Methode
  • ParameterExpression-Objekt für den Zugriff / die Definition eines Funktionsparameters
  • NewExpression-Objekt für das Erstellen einer Objektinstanz

Alle weiteren möglichen Objekte findet man im Namespace System.Linq.Expressions. Verschachtelt in einer Hierarchie können verschiedene Programmlogiken definiert werden. So kann etwa per MethodCallExpression Objekt ein Methodenaufruf definiert werden, dem per ParameterExpression Objekt ein Übergabeparameter mitgegeben wird. Allerdings haben ExpressionTrees unter .Net 3.5 eine wesentliche Beschränkung: Es können keine Blöcke abgebildet werden. Damit sind auch Kontrollstrukturen wie If-Then-Else oder Foreach von vorne herein ausgeschlossen. Dies gilt aber ab .Net 4.0 nicht mehr, denn ab dieser Version gibt es zusätzliche Klassen wie BlockExpression und viele andere.

Als kurzen ersten Versuch mit ExpressionTrees habe ich zwei Methoden geschrieben, die jeweils eine Dynamische Methode erstellen, mit deren Hilfe ein Konstruktor eines übergebenen Typen aufgerufen werden kann. Die hiermit erzeugten Methoden können als quasi-Alternative zu Activator.CreateInstance gesehen werden.

  1. /// <summary>
  2. /// Create dynamic instance creator.
  3. /// </summary>
  4. private static Func<TResult> CreateDynamicInstanceCreator<TResult>()
  5. {
  6. Type resultType = typeof(TResult);
  7.  
  8. //Get constructor
  9. ConstructorInfo constructor = resultType.GetConstructor(new Type[0]);
  10. if (constructor == null)
  11. {
  12. throw new ArgumentException(
  13. "No matching constructor found in result type!",
  14. "TResult");
  15. }
  16.  
  17. //Create dynamic method
  18. Expression<Func<TResult>> result = Expression.Lambda<Func<TResult>>(
  19. Expression.New(constructor));
  20. return result.Compile();
  21. }
  22.  
  23. /// <summary>
  24. /// Create a dynamic instance creator.
  25. /// </summary>
  26. private static Func<TArgument1, TResult> CreateDynamicInstanceCreator<TArgument1, TResult>()
  27. {
  28. Type resultType = typeof(TResult);
  29. Type argument1Type = typeof(TArgument1);
  30.  
  31. //Get constructor
  32. Type[] argumentArray = new Type[] { argument1Type };
  33. ConstructorInfo constructor = resultType.GetConstructor(argumentArray);
  34. if (constructor == null)
  35. {
  36. throw new ArgumentException(
  37. "No matching constructor found in result type!",
  38. "TResult");
  39. }
  40.  
  41. //Create dynamic method
  42. ParameterExpression param1 = Expression.Parameter(argument1Type, "arg");
  43. Expression<Func<TArgument1, TResult>> result = Expression.Lambda<Func<TArgument1, TResult>>(
  44. Expression.New(
  45. constructor,
  46. param1),
  47. param1);
  48. return result.Compile();
  49. }

Der Typ-Parameter TResult bestimmt die Klasse, von dem ein Objekt erzeugt werden soll. In der Zweiten Methode kann zusätzlich per TArgument1 der Typ des ersten Parameters des Konstruktors übergeben werden. Zurückgegeben werden in beiden Fällen Delegaten vom Type Func<...>, die anschließend bei jedem Aufruf ein neues Objekt vom angegebenen Typ erzeugen. Die jeweils nötigen ExpressionTrees (jeweils am Ende der Methoden) sind überraschenderweise sehr schlank.

 

Mit dieser Funktion kann man also sehr viel anstellen, und vor allem solche Stellen in der eigenen Anwendung beschleunigen, wo sehr viel mit System.Reflection gemacht wird. Die große Frage ist, ob sich ein solcher Aufwand am Ende lohnt, und wie zahlreich die Einsatzmöglichkeiten am Ende wirklich sind. Interessant finde ich die Erweiterungen im neuen .Net 4.0, mithilfe von BlockExpression könnte man in Verbindung mit dem System.Reflection.Emit ohne großen Aufwand auch ganze Klassenhierarchien dynamisch zur Laufzeit erzeugen - ohne sich mit IL-Code auseinandersetzen zu müssen.

 

Kommentar hinzufügen

Ihr Name:
Kommentar: