2011-04-22 5 views
17

사용자가 임의의 식을 입력 할 수있는 라이브러리 작업을하고 있습니다. 그런 다음 내 라이브러리는 위의 표현식을 델리게이트로 컴파일합니다. 자, 아직 알려지지 않은 이유로 Compile을 사용하여 표현식을 컴파일하는 경우가 종종 있습니다. 컴파일 된 표현식이 아닌 경우보다 훨씬 느린 코드가 생성됩니다. I asked a question about this 전에 한 가지 해결 방법은 Compile이 아니라 CompileToMethod을 사용하지 않고 새로운 동적 어셈블리의 새 형식에 static 메서드를 만드는 것입니다. 그건 효과가 있고 코드는 빠릅니다..NET : 동적 어셈블리에서 비공개 멤버에 액세스

그러나 사용자가 임의의 식을 입력 할 수 있으며 사용자가 비공개 함수를 호출하거나 식에서 비공개 필드에 액세스하면 System.MethodAccessException을 throw합니다 (비공개 메서드의 경우) 대리자가 호출 될 때

는 내가 아마 여기에 할 수있는 것은 표현 아무것도 아닌 대중과 이러한 경우에 Compile 느린을 사용하여 액세스하는 경우 확인하는 새로운 ExpressionVisitor을 만드는 것입니다,하지만 난 오히려 동적 어셈블리가 어떻게 든 액세스 권한을 얻을 수 있음을 가질 것 비공개 회원. 아니면 내가 할 수있는 일이 있다면 Compile (때때로) 느려지는지 확인하십시오.

전체 코드

이 문제를 재현하는 방법 :

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Linq.Expressions; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace DynamicAssembly 
{ 
    public class Program 
    { 
    private static int GetValue() 
    { 
     return 1; 
    } 

    public static int GetValuePublic() 
    { 
     return 1; 
    } 

    public static int Foo; 

    static void Main(string[] args) 
    { 
     Expression<Func<int>> expression =() => 10 + GetValue(); 

     Foo = expression.Compile()(); 

     Console.WriteLine("This works, value: " + Foo); 

     Expression<Func<int>> expressionPublic =() => 10 + GetValuePublic(); 

     var compiledDynamicAssemblyPublic = (Func<int>)CompileExpression(expressionPublic); 

     Foo = compiledDynamicAssemblyPublic(); 

     Console.WriteLine("This works too, value: " + Foo); 

     var compiledDynamicAssemblyNonPublic = (Func<int>)CompileExpression(expression); 

     Console.WriteLine("This crashes"); 

     Foo = compiledDynamicAssemblyNonPublic(); 
    } 

    static Delegate CompileExpression(LambdaExpression expression) 
    { 
     var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
     new AssemblyName("MyAssembly"+ Guid.NewGuid().ToString("N")), 
     AssemblyBuilderAccess.Run); 

     var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module"); 

     var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public); 

     var methodBuilder = typeBuilder.DefineMethod("MyMethod", 
     MethodAttributes.Public | MethodAttributes.Static); 

     expression.CompileToMethod(methodBuilder); 

     var resultingType = typeBuilder.CreateType(); 

     var function = Delegate.CreateDelegate(expression.Type, 
     resultingType.GetMethod("MyMethod")); 

     return function; 
    } 
    } 
} 
+0

답변이 없지만 개인용 메서드 호출을 지원해야하는 이유는 무엇입니까? – jlew

+0

사용자가 가능해야하기 때문에. '() => CallPrivateMethod()'와 같이 식을 생성 할 때 * 액세스 할 수 있기 때문에 런타임에 실패합니다. 그에게 달리기 전까지는 작동하지 않으며 충돌하고 화상을 입는다는 것을 나타내는 것은 아무것도 없습니다. 그건 정말 나쁘고 "최소한의 놀라움"에 대한 규칙을 어기므로 그렇게하는 것이 정당화 될 수 없으며 느린 코드에 정착해야합니다. – JulianR

+0

사용자가 C# 프로그래머 인 경우 (예 : 양식에 표현식을 입력하는 것과는 대조적으로) 의미가 있습니다. 컴파일 된 델리게이트에 대한 릴리즈와 디버깅 모드를 벤치마킹 해 보았습니까? 그들은 어떻게 서로 비교합니까? – jlew

답변

5

비공개 필드 나 다른 클래스의 멤버에 리플렉션없이 액세스 할 수있는 권한이 없으므로이 문제는 사용 권한이 아닙니다. 이는 두 개의 비 동적 어셈블리를 컴파일하고 두 번째 어셈블리에서 하나의 어셈블리가 공용 메서드를 호출하는 것과 유사합니다. 그런 다음 첫 번째 어셈블리를 다시 컴파일하지 않고이 메서드를 private로 변경하면 런타임에 첫 번째 어셈블리 호출이 이 실패합니다. 즉, 동적 어셈블리의 표현식은 동일한 어셈블리에서도 다른 클래스에서 수행하는 것보다 더 이상 호출 할 수있는 권한이없는 일반 메서드 호출로 컴파일됩니다.

권한이 없기 때문에 비공개 필드 및 메서드 참조를 리플렉션을 사용하는 하위 표현식으로 변환 할 수 있습니다.

다음은 테스트 사례에서 가져온 예제입니다.

Expression<Func<int>> expression =() => 10 + GetValue(); 

을하지만 성공할 :이 실패이 이후

Expression<Func<int>> expression =() => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); 

는 예외가 충돌하지 않습니다, 당신은 동적 어셈블리가 반사 권한을 가지고 있음을 알 수 있으며 민간 방법에 액세스 할 수 있습니다 실제로는 CompileToMethod이라는 일반적인 메서드 호출을 사용하여 수행 할 수 없습니다.

1

내가 한 번 DynamicMethod 사용하여 생성 된 IL 코드에서 클래스의 개인 요소를 액세스하는 문제가 있었다.

http://msdn.microsoft.com/en-us/library/exczf7b9.aspx

이 링크에 액세스하는 방법의 샘플이 포함되어 있습니다 :

그것은 느릅 나무 개인 액세스에 클래스의 유형을 수신 DynamicMethod 허용 될 클래스의 생성자의 과부하가 있다고 밝혀졌다 개인 데이터 ... 이것은 표현식 나무와는 아무런 관련이 없다는 것을 알고 있지만, 어떻게해야 할 지에 대한 단서를 줄 수 있습니다.

식 트리를 컴파일 할 때 비슷한 점이 있거나 또는 해당 식 트리를 DynamicMethod로 만들 수 있습니다.

+0

. 그러나'CompileToMethod'는 오직 한가지 방법 만이 호출됩니다 : 그것은 MethodBuilder의 인스턴스를 전달해야합니다. 그리고 당신은'TypeBuilder'에서만'MethodBuilder'를 얻을 수 있습니다. 그리고 당신은 그것을위한'ModuleBuilder'가 필요합니다. * 당신은'AssemblyBuilder'가 필요합니다. – JulianR

1

동적 어셈블리가 아닌 경우 동적 어셈블리에 InternalsVisibleTo을 실제로 포함 할 수 있습니다 (강력한 이름으로도 작동합니다).). 그것은 귀하의 경우 충분할 수 있습니다 내부 회원을 사용하여 허용할까요?

여기에 다른 어셈블리에서 내부 재료를 사용하는 MOQ의 동적 어셈블리를 사용할 수 있도록 뜨거운 보여주는 예입니다, 아이디어를 얻으려면이 방법이 충분하지 않은 경우 http://blog.ashmind.com/2008/05/09/mocking-internal-interfaces-with-moq/

, 나는 조합으로 갈 것 Rick과 Miguel의 제안 : public이 아닌 멤버에 대한 각 호출에 대해 "proxy"DynamicMethod를 만들고 원래 호출 대신 표현 트리를 변경합니다.

관련 문제