2010-08-19 4 views
5

Ich arbeite an einem Projekt, um Tokenized benutzerdefinierte Ausdrücke unterschiedlicher Komplexität zu bewerten, die C# als die Skriptsprache verwenden.Wie kann man einen literalen Ausdruck mit Reflection.Emit injizieren?

Ich habe ein Arbeitsmodell mit CodeDOM und Reflexion zum Generieren einer Evaluator-Klasse, erstellen und laden Sie die Assembly (GenerateInMemory = true), instanziieren Sie die Klasse und führen Sie die Evaluierungsmethode aus. Ich möchte jedoch die Assembly in einer AppDomain laden, damit ich sie entladen kann, wenn die Ausführung abgeschlossen ist. Bei der Untersuchung dieses Problems wurde ich auf die AppDomain.DefineDynamicAssembly-Methode verwiesen. Dies scheint genau das zu sein, was ich brauche, da ich eine Sammlerversammlung erstellen kann.

Hier sind ein paar Beispiele für die benutzerdefinierte Ausdrücke, und die Klassen von meinem CodeDOM Projekt generieren:

Einfacher benutzerdefinierter Ausdruck:

return Abs(@[email protected]/@[email protected] * 5.5); 

generierte Klasse:

namespace Lab.ResultProcessing 
{ 

    public sealed class ExpressionEvaluator 
    { 
     public double Evaluate() 
     { 
      return System.Math.Abs(449.86881550861/74.934407754305 * 5.5); 
     } 
    } 
} 

Komplexerer benutzerdefinierter Ausdruck:

double GFR; 
double MA_GFR; 
double MB_GFR; 
double FA_GFR; 
double FB_GFR; 

GFR = (170 * 
     Pow(@[email protected], -0.999) * 
     Pow(@[email protected], -0.176) * 
     Pow(@[email protected], -0.170) * 
     Pow(@[email protected], 0.318)); 

MA_GFR = GFR; 
MB_GFR = GFR * 1.180; 
FA_GFR = GFR * 0.762; 
FB_GFR = GFR * 1.180 * 0.762; 

if (("@[email protected]" != "B") && ("@GEND[email protected]" == "M")) 
{ 
    return MA_GFR; 
} 
else if (("@[email protected]" == "B") && ("@[email protected]" == "M")) 
{ 
    return MB_GFR; 
} 
else if (("@[email protected]" != "B") && ("@[email protected]" == "F")) 
{ 
    return FA_GFR; 
} 
else if (("@[email protected]" == "B") && ("@[email protected]" == "F")) 
{ 
    return FB_GFR; 
} 
else 
{ 
    return GFR; 
} 

generiert Klasse:

namespace Lab.ResultProcessing 
{ 

    public sealed class ExpressionEvaluator 
    { 
     public double Evaluate() 
     { 
      double GFR; 
double MA_GFR; 
double MB_GFR; 
double FA_GFR; 
double FB_GFR; 

GFR = (170 * 
     System.Math.Pow(0.797258181752292, -0.999) *  
     System.Math.Pow(63.6814545438073, -0.176) * 
     System.Math.Pow(5.47258181752292, -0.170) *  
     System.Math.Pow(3.79725818175229, 0.318));  

MA_GFR = GFR;         
MB_GFR = GFR * 1.180;       
FA_GFR = GFR * 0.762;       
FB_GFR = GFR * 1.180 * 0.762;     

if (("B" != "B") && ("M" == "M")) 
{ 
    return MA_GFR;        
} 
else if (("B" == "B") && ("M" == "M")) 
{ 
    return MB_GFR;        
} 
else if (("B" != "B") && ("M" == "F")) 
{ 
    return FA_GFR;        
} 
else if (("B" == "B") && ("M" == "F")) 
{ 
    return FB_GFR;        
} 
else 
{ 
    return GFR; 
} 
; 
     } 
    } 
} 

ich jetzt versuchen, die Funktionalität, die oben mit Reflection.Emit beschrieben zu duplizieren. Mein Problem ist, dass ich keinen Weg gefunden habe, die entschlüsselte Formel in die emittierte Klasse zu injizieren. Hier

ist der Code, ich verwende:

public static object DynamicEvaluate2(string expression) 
{ 
    AssemblyName assemblyName = new AssemblyName("Lab.ResultProcessing"); 
    AppDomain appDomain = AppDomain.CurrentDomain; 
    AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect); 
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); 
    TypeBuilder typeBuilder = moduleBuilder.DefineType("ExpressionEvaluator", TypeAttributes.Sealed); 
    MethodBuilder methodBuilder = typeBuilder.DefineMethod("Evaluate", MethodAttributes.Public | MethodAttributes.Final, typeof(double), null); 
    ILGenerator methodGenerator = methodBuilder.GetILGenerator(); 

    methodGenerator.Emit(OpCodes.Ldobj, expression); 
    methodGenerator.Emit(OpCodes.Ret); 

    Type evaluatorType = typeBuilder.CreateType(); 
    MethodInfo methodInfo = evaluatorType.GetMethod("Evaluate"); 

    object evaluator = Activator.CreateInstance(evaluatorType); 
    object result = methodInfo.Invoke(evaluator, null); 

    return result; 
} 

Wenn die MethodInfo.Invoke Methode aufgerufen wird, ich die folgende Fehlermeldung erhalten:

Prüfnorm ResultCalculatorTest.ResultCalculatorClassFactoryTest.DynamicEvaluate2Test warf Ausnahme: -System. Reflection.TargetInvocationException: Vom Ziel eines Aufrufs wurde eine Ausnahme ausgelöst. ---> System.BadImageFormatException: Schlechtes Klassen-Token.

Also habe ich ein paar Fragen:

Wie kann die detokenized benutzerdefinierten Ausdruck in injizieren Reflection.Emit mit?
Gibt es eine Möglichkeit, C# -Code für die emittierte Klasse oder nur in IL zu sehen?
Wie debugge ich die emittierte Klasse?

Jede Hilfe würde sehr geschätzt werden.

+0

Was ist der 'Ausdruck'-String? –

+0

Die ausgedrückte Zeichenkette wäre ähnlich zu den oben aufgeführten Beispielen, wie beispielsweise "Zurück Abs (@HDL @/@ LDL @ * 5.5);" aber es könnte viel komplexer sein. –

+1

Sie * können * CodeDom mit Ihrer eigenen AppDomain verwenden - CodeDom spuckt eine tatsächliche .dll aus, die Sie natürlich in Ihre AppDomain laden können. (Auch in Ihrem Beispiel verwenden Sie keine separate AppDomain) –

Antwort

5
methodGenerator.Emit(OpCodes.Ldobj, expression); 

Das tun nicht, was Sie es haben wollen: die ldobj Anweisung erwartet ein Type, kein string. Laut MSDN ist der Zweck der ldobj Anweisung zu copy the value type object pointed to by an address.

Im Gegensatz zu CodeDom analysiert Reflection.Emit Ihren Ausdruck nicht für Sie. Ihr Code muss die Zeichenfolge expression parsen und die richtige Sequenz von IL-Opcodes ausgeben, um diesen Ausdruck zu berechnen. In der Tat müssen Sie Ihren eigenen Compiler schreiben.

Eine Alternative zur Reflexion.Emit ist die Typen in System.Linq.Expressions. Diese sind höher als Reflection.Emit und niedriger als CodeDom. Sie müssen Ihre Zeichenfolge weiterhin analysieren, aber Sie erstellen einen abstrakten Syntaxbaum im Speicher, anstatt rohe Opcodes auszugeben.

+0

Ich hatte davor Angst. Ich möchte die benutzerdefinierten Ausdrücke nicht analysieren. Ich komme auf mein ursprüngliches Projekt zurück. –

Verwandte Themen