2013-03-20 5 views
1

Reflection.Emit을 사용하여 어셈블리를 만들고 있는데 특수 콜백을 호출하려고합니다. 예상대로Reflection 생성 어셈블리에서 람다 호출

public void Call(ILGenerator il, Delegate action) 
{ 
    il.Emit(OpCodes.Call, action.Method); 
} 

public static void DoStuff() 
{ 
    Console.WriteLine("Action invoked!"); 
} 

Call(CurrentMethod.ILGenerator, DoStuff); 

이 코드는 바로 작동합니다

다음은 코드의 단순화 된 버전입니다.

그러나,이 같은 람다 식을 전달할 :

Call(CurrentMethod.ILGenerator,() => Console.WriteLine("test")); 

다음의 예외가 발생이 시간 :

System.MethodAccessException : 법에 의해 시도 '.RUN() '메서드에 액세스하려면'Compiler.Test.ImportedFunctions.b__0() '이 (가) 실패했습니다.

해결 방법이 있습니까?

+0

코드가 완전하게 신뢰되고 있습니까? – Greg

+0

@Greg, 어셈블리는'AppDomain.CurrentDomain.DefineDynamicAssembly (name, AssemblyBuilderAccess.RunAndSave)'로 생성됩니다. 신뢰 설정을 어떻게 확인합니까? – Impworks

+0

@Impworks : 당신이 이미 할 수 있다면, 당신은 완전 신뢰하에 이미 실행 중입니다 :) – leppie

답변

1

Delegate 너무 일반적입니다. Action을 시도해보십시오.

경고!

대리자의 target 속성이 null이 아니면 불가능합니다.

대상 필드의 값을 정적 필드에 임시로 저장하여이 문제를 해결할 수 있습니다.

가능한 해결 방법 (방출 수식) : 아니오 재귀 호출로 단일 스레드 환경에서 실행하는 경우

class Foo { static object target; } 

public void Call(ILGenerator il, Action action) 
{ 
    Foo.target = action.Target; 
    il.Emit(OpCodes.Ldsfld, typeof(Foo).GetField("target"); 
    il.Emit(OpCodes.Callvirt, action.Method); 
} 

이 작동합니다.

재귀 환경에서는 Foo.target에 동적 바인딩을 사용해야합니다.이 바인딩은 C#에서 사용할 수 없습니다.

운 좋게 I have written such a facility for C#.

+0

불행히도, 람다에서 생성 된'Func <>'과'Action <>'객체는 정적이지 않습니다. 나는 당신의 시설을 시험해 볼 것입니다. – Impworks

+1

대리자가 변수를 캡처하는 경우에만 정적이 아닙니다. – leppie

관련 문제