2010-08-19 3 views
5

저는 C#을 스크립팅 언어로 사용하여 토큰 화 된 다양한 사용자 정의 표현식을 평가하는 프로젝트를 진행하고 있습니다.어떻게 Reflection.Emit을 사용하여 리터럴 식을 삽입 할 수 있습니까?

나는 Evaluator 클래스를 생성하고 어셈블리를 생성 및로드하고 (GenerateInMemory = true) CodeDOM 및 리플렉션을 사용하여 클래스를 인스턴스화하고 평가 메서드를 실행하는 작업 모델을 사용합니다. 그러나 AppDomain에 어셈블리를로드하여 실행이 완료되면 언로드 할 수 있습니다. 이 문제를 연구하는 동안 AppDomain.DefineDynamicAssembly 메서드로 이동했습니다. 이것은 소장품 조립품을 만들 수 있기 때문에 제가 필요로하는 것 같습니다. 여기

은 사용자 정의 식의 몇 가지 예이며, 클래스 내 CodeDOM을 프로젝트에 의해 생성 :

간단한 사용자 정의 표현 :

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

생성 된 클래스 :

namespace Lab.ResultProcessing 
{ 

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

더 복잡한 사용자 정의 표현식 :

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; 
} 
012 3,516,

생성 된 클래스 :

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

는 지금 Reflection.Emit를를 사용하여 위에서 설명한 기능을 복제하려고 시도하고있다. 내 문제는 내가 detokenized 수식을 방출 클래스에 삽입하는 방법을 찾지 못했다는 것입니다. 여기

내가 사용하고있는 코드입니다 시스템 :

시험 방법 ResultCalculatorTest.ResultCalculatorClassFactoryTest.DynamicEvaluate2Test는 예외를 던진 다음 methodInfo.Invoke 방법 나는 다음과 같은 오류라고

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 : 호출 타겟에 의해 예외가 Throw되었습니다. ---> System.BadImageFormatException : 클래스 토큰이 잘못되었습니다.

방법에 Reflection.Emit를를 사용하여 detokenized 사용자 정의 표현을 삽입 할 수 있습니다

그래서 나는 몇 가지 질문이 있습니까?
방출 된 클래스의 C# 코드를 볼 수있는 방법이 있습니까? 아니면 IL에서만입니까?
방출 된 클래스를 어떻게 디버깅합니까?

도움을 주시면 감사하겠습니다.

+0

'표현식'문자열이란 무엇입니까? –

+0

표현 문자열은 "return Abs (@HDL @/@ LDL @ * 5.5);"와 같은 위에 나열된 예와 비슷합니다. 그러나 훨씬 더 복잡 할 수 있습니다. –

+1

자신의 AppDomain과 함께 CodeDom을 사용할 수 있습니다 - CodeDom은 AppDomain으로로드 할 수있는 실제 .dll을 출력합니다. (예를 들어, 사용자의 예에서 별도의 AppDomain을 사용하지 않고 있습니다.) –

답변

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

이 당신이 원하는 것을하지 않는 다음 ldobj 명령은 Type 아닌 string 기대하고있다. MSDN에 따르면 ldobj 명령의 목적은 copy the value type object pointed to by an address입니다.

CodeDom과 달리 Reflection.Emit은 표현을 구문 분석하지 않습니다. 코드는 expression 문자열을 구문 분석하고 해당 연산을 계산하기 위해 IL 연산 코드의 올바른 순서를 방출해야합니다. 실제로, 당신은 당신 자신의 컴파일러를 작성해야한다.

반사에 대한 대안.방출 유형은 System.Linq.Expressions입니다. 이는 Reflection.Emit보다 높은 레벨이며 CodeDom보다 낮은 레벨입니다. 여전히 문자열을 구문 분석해야하지만 원시 opcode를 내보내는 대신 메모리에 추상 구문 트리를 작성합니다.

+0

나는 그것을 두려워했다. 사용자 정의 식을 구문 분석하지 않으려합니다. 나는 원래 프로젝트로 되돌아 가고있다. –

관련 문제