2009-04-22 11 views
19

메서드 호출식이 있고 메서드를 호출하려고합니다. 방법을 찾아 냈지만 모든 인수가 ConstantExpression으로 설명되지 않았기 때문에 매개 변수 값을 검색하는 데 문제가 있습니다.MethodCallExpression에서 메서드를 호출하는 방법 #

Expression<Action<T>> = t => t.DoSomething(Par0, Par1, Par2); 
MethodCallExpression methodCallExpression = selector.Body 
               as MethodCallExpression; 

// get the information which is needed to invoke the method from the provided 
// lambda expression. 
MethodInfo methodInfo = methodCallExpression.Method; 
object[] arguments = methodCallExpression.Arguments.OfType<ConstantExpression>() 
          .Select(p => p.Value).ToArray(); 

// invoke the expression on every item within the enumerable 
foreach (TSource item in source) 
{ 
    methodInfo.Invoke(item, arguments); 
} 

또한, 지금 내가 할 수있는 올바른 방법이 무엇인지 확실하지 않다, 메소드를 호출하는 다른 방법을 보았다.

var func = expression.Compile(); 
var success = func.Invoke(); 

그럼 내 질문은 어떻게 methodCallExpression.Arguments에서 메서드 인수 값을 검색 할 수 있습니까?

내 목표를 달성하기위한 더 쉬운 방법이 있습니까?

답변

21

인수를 검색하고 MethodInfo를 직접 호출하는 것에 대해 걱정할 필요가 없습니다. .NET에서 처리하도록 할 수 있습니다. 그 메소드를 포함하는 람다 식을 생성하기 만하면됩니다.

예 :

MethodCallExpression expression = GetExpressionSomeHow(); 
object result = Expression.Lambda(expression).Compile().DynamicInvoke(); 

어쨌든 내 Linq 공급자에서 중첩 된 쿼리를 처리하는 방법입니다.

EDIT : 실제로는 선택기 변수에 이미 LambdaExpression이있는 것 같습니다. 이 경우, 당신은 단지 컴파일하고 직접 호출 할 수 있어야한다 :

object result = selector.Compile().DynamicInvoke(); 
+2

감사합니다. 훨씬 쉽습니다. 나는 지금 이렇게하고있다 : // 호출 용 델리게이트를 얻기 위해 람다 표현식을 컴파일한다. 액션 동작 = selector.Compile(); // 열거 형 내의 모든 항목에 대해 표현식 호출 foreach (소스의 TSource 항목) { 작업 (항목); } 그리고 마지막으로이 문제에 대한 msdn 설명서도 있습니다. http://msdn.microsoft.com/en-us/library/bb882536.aspx – Enyra

+0

'selector.Compile'할 수없는 이유가 있습니까?()()'? 왜 괄호가 작동하면'Invoke' 또는'DynamicInvoke'가 필요합니까? – ErikE

6

표현은 매우 집약적 인 작업이다 컴파일, 그래서 나는 당신이 계획하는 경우 표현을 다시 사용하여 그렇게 만 것입니다. 그렇지 않으면 반사 방식을 추천합니다. 당신은 그것이 더 빨리 실행된다는 것을 알게 될 것입니다. expression.Compile()을 엄격한 루프로 호출하지 마십시오.

2

@ Ch00k < - 감사합니다. 좋은 설명입니다. 그저 추가하고 싶습니다.

selector.Compile(); 

은 대의원입니다. 인스턴스 메소드의 경우이 메소드를 호출 할 인스턴스가 필요합니다. 당신은

// Grab the method from MyClass - param1 and param2 are the actual parameters you 
// want to pass to the method call. 
Expression<Func<MyClass, TValue>> selector = (x => x.MyMethod(param1, param2)); 

// Create an instance of MyClass to call the method on 
var myClass = new MyClass(); 

// Call the method on myClass through DynamicInvoke 
object returnValue = selector.Compile().DynamicInvoke(myClass); 
0

내가 개체를 반환하기 위해 시도 할 람 DynamicInvoke하기 위해 인수로이 인스턴스를 전달합니다

LambdaExpression l = Expression.Lambda(Expression.Convert(element, element.Type)); 
return l.Compile().DynamicInvoke(); 
1

경우 :

private static object _getValue(MethodCallExpression expression) 
{ 
    var objectMember = Expression.Convert(expression, typeof(object)); 

    var getterLambda = Expression.Lambda<Func<object>>(objectMember); 

    var getter = getterLambda.Compile(); 

    return getter(); 
} 

은 다음을 호출 할 수 있습니다 훨씬 빠릅니다 expression.call을 Action 또는 Func으로 컴파일하려면 다음과 같이하십시오.

var method = typeof(MyType).GetMethod(nameof(MyType.MyMethod), BindingFlags.Public | BindingFlags.Static); 
var parameter = Expression.Parameter(typeof(string), "s"); 
var call = Expression.Call(method, parameter); 
var lambda = Expression.Lambda<Func<string, int>>(call, call.Arguments.OfType<ParameterExpression>()); 
var func = lambda.Compile(); 
int result = func("sample string input"); 

이렇게하면 단순히 func.Invoke ("mystring") 또는 func ("my string");

여기 비밀은 Expression.Call을 만들 때 사용한 것과 동일한 매개 변수를 전달해야합니다. 그렇지 않으면 범위 '에서 참조 된'System.String '유형의 "InvalidOperationException"변수's '유형의 오류가 발생합니다. , 그러나 그것은 정의되지 않았다.

관련 문제