Wie erstelle ich ein kompilierte Lambda mit Methodenaufrufen?
-
11-07-2019 - |
Frage
Ich bin Erzeugung kompilierte Getter-Methoden zur Laufzeit für ein bestimmtes Mitglied. Gerade jetzt, mein Code geht davon aus, dass nur das Ergebnis der Getter-Methode ist ein String (arbeitete gut zum Testen). Allerdings würde ich diese Arbeit mit einer benutzerdefinierten Konverter Klasse machen wie ich geschrieben habe, siehe unten, „ConverterBase“ Referenz, die ich hinzugefügt haben.
ich kann nicht herausfinden, wie der Anruf an die Konverter-Klasse meines Ausdrucksbaum hinzuzufügen.
public Func<U, string> GetGetter<U>(MemberInfo info)
{
Type t = null;
if (info is PropertyInfo)
{
t = ((PropertyInfo)info).PropertyType;
}
else if (info is FieldInfo)
{
t = ((FieldInfo)info).FieldType;
}
else
{
throw new Exception("Unknown member type");
}
//TODO, replace with ability to specify in custom attribute
ConverterBase typeConverter = new ConverterBase();
ParameterExpression target = Expression.Parameter(typeof(U), "target");
MemberExpression memberAccess = Expression.MakeMemberAccess(target, info);
//TODO here, make the expression call "typeConverter.FieldToString(fieldValue)"
LambdaExpression getter = Expression.Lambda(memberAccess, target);
return (Func<U, string>)getter.Compile();
}
Ich bin auf der Suche für das, was in dem zweiten TODO Bereich setzen (ich kann die ersten Griff:)).
Das resultierende kompilierte lambda sollte eine Instanz des Typs U als param nehmen, rufen die angegebene Mitglied Zugriffsfunktion, dann ist die „FieldToString“ -Methode mit dem Ergebnis des Wandlers nennen, und gibt die resultierende Zeichenfolge.
Lösung
Können Sie erläutern, was (wenn es regelmäßige C # war) Sie den Ausdruck auswerten möchten? Ich kann den Ausdruck leicht genug schreiben - ich einfach nicht vollständig die Frage verstehen ...
(edit re Kommentar) - in diesem Fall wird es so etwas wie:
ConverterBase typeConverter = new ConverterBase();
var target = Expression.Parameter(typeof(U), "target");
var getter = Expression.MakeMemberAccess(target, info);
var converter = Expression.Constant(typeConverter, typeof(ConverterBase));
return Expression.Lambda<Func<U, string>>(
Expression.Call(converter, typeof(ConverterBase).GetMethod("FieldToString"),
getter), target).Compile();
Oder, wenn der Typ zu binden ablehnt, werden Sie eine Besetzung / convert injizieren müssen:
MethodInfo method = typeof(ConverterBase).GetMethod("FieldToString");
return Expression.Lambda<Func<U, string>>(
Expression.Call(converter, method,
Expression.Convert(getter, method.GetParameters().Single().ParameterType)),
target).Compile();
Andere Tipps
Sie müssen das Objekt in einem ExpressionConstant wickeln, z.B. von Expression.Constant verwenden. Hier ein Beispiel:
class MyConverter
{
public string MyToString(int x)
{
return x.ToString();
}
}
static void Main()
{
MyConverter c = new MyConverter();
ParameterExpression p = Expression.Parameter(typeof(int), "p");
LambdaExpression intToStr = Expression.Lambda(
Expression.Call(
Expression.Constant(c),
c.GetType().GetMethod("MyToString"),
p),
p);
Func<int,string> f = (Func<int,string>) intToStr.Compile();
Console.WriteLine(f(42));
Console.ReadLine();
}