2010-08-19 11 views
5

C# kullanarak kodlama dili olarak değişen karmaşıklık kullanıcı tanımlı tanımlanmış ifadeler değerlendirmek için bir proje üzerinde çalışıyorum.Reflection.Emit kullanarak bir hazır ifade nasıl enjekte edilebilir?

Bir değerlendirici sınıfı oluşturmak, derleme oluşturmak ve yüklemek (GenerateInMemory = true), sınıfı başlatmak ve değerlendirme yöntemini yürütmek için CodeDOM ve yansımayı kullanan bir çalışma modelim var. Ancak, derlemeyi bir AppDomain'e yüklemek istiyorum, böylece yürütme tamamlandığında boşaltabilirim. Bu sorunu araştırırken, AppDomain.DefineDynamicAssembly yöntemine yönlendirildim. Bu, tahsil edilebilir bir meclis oluşturabildiğim için tam olarak ihtiyacım olan şey gibi görünüyor. İşte

kullanıcı tanımlı ifadelerin birkaç örnek vardır ve sınıflar benim CodeDOM proje tarafından oluşturulan:

Basit kullanıcı tanımlı ifadesi:

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

Oluşturulan sınıfı:

namespace Lab.ResultProcessing 
{ 

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

daha karmaşık kullanıcı tanımlı ifadesi:

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") && ("@[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; 
} 

Oluşturulan sınıfı:

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; 
} 
; 
     } 
    } 
} 

şimdi Reflection.Emit kullanarak yukarıda açıklanan işlevselliği çoğaltmak için çalışıyorum. Benim problemim, detokenized formülü yayılan sınıfa enjekte etmenin bir yolunu bulamadım. İşte

kullanıyorum kodudur: Sistemi:

Deney yöntemi ResultCalculatorTest.ResultCalculatorClassFactoryTest.DynamicEvaluate2Test istisna attı: MethodInfo.Invoke yöntemi aşağıdaki hatayı alıyorum denir

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; 
} 

. Reflection.TargetInvocationException: İstisnanın hedefi tarafından istisna atıldı. ---> System.BadImageFormatException: Kötü sınıf belirteci.

Nasıl Reflection.Emit kullanılarak detokenized kullanıcı tanımlı ifadesini enjekte edebilir

yüzden birkaç soru var?
Yayılan sınıf için C# kodunu görmenin bir yolu var mı, yoksa sadece IL?
Yayılan sınıfı nasıl ayıklarim?

Herhangi bir yardım büyük memnuniyetle karşılanacaktır.

+0

'expression' dizesi nedir? –

+0

İfade dizgisi yukarıda listelenen örneklere benzerdir, örneğin "return Abs (@HDL @/@ LDL @ * 5.5);" ama çok daha karmaşık olabilirdi. –

+1

* * Kendi AppDomain ile CodeDom kullanabilirsiniz * CodeDom elbette AppDomain yüklemek için devam edebilirsiniz gerçek bir .dll dışarı tükürecektir. (Ayrıca, örneğinizde, ayrı bir AppDomain kullanmıyorsunuz) –

cevap

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

Bu onu istediğini yapmaz: ldobj talimat bir Type değil, bir string bekliyor. MSDN'ye göre, ldobj talimatının amacı copy the value type object pointed to by an address.

CodeDom'dan farklı olarak, Reflection.Emit ifadenizi sizin için ayrıştırmaz. Kodunuzun expression dizesini ayrıştırması ve bu ifadeyi hesaplamak için doğru IL opcodes dizisini göndermesi gerekir. Aslında kendi derleyicinizi yazmanız gerekiyor.

Yansımaya bir alternatif.Emit, System.Linq.Expressions'daki türlerdir. Bunlar, Reflection.Emit'ten daha yüksek ve CodeDom'dan daha düşük seviyededir. Dizginizi hala ayrıştırmanız gerekecek, ancak ham opcodes yayınlamak yerine bellekteki soyut bir sözdizimi ağacı oluşturursunuz.

+0

Bundan korkuyordum. Kullanıcı tanımlı ifadeleri ayrıştırmak istemiyorum. Orijinal projeme geri dönüyorum. –

İlgili konular