2008-12-04 2 views
1

Я пытаюсь узнать о выражении дерев, и я создал метод, который принимаетДоступа вызывающий объект из MethodCallExpression

Expression<Func<bool>> 

и выполняет его, если он удовлетворяет некоторые условия - см код ниже.

 private static void TryCommand(Expression<Func<bool>> expression) 
     { 
      var methodCallExpression = expression.Body as MethodCallExpression; 
      if (methodCallExpression == null) 
      { 
       throw new ArgumentException("expression must be a MethodCallExpression."); 
      } 

      if (methodCallExpression.Object.Type != typeof (MyClass)) 
      { 
       throw new ArgumentException("expression must be operating on an instanceof MyClass.");     
      } 

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

      if(!success) 
      { 
       Console.WriteLine(methodCallExpression.Method.Name + "() failed with error code " + (func.Target as MyClass).GetError()); 
      } 
     } 

Проблема,

(func.Target as MyClass) 

равна нулю. Ясно, что я делаю что-то неправильно! Как получить доступ к экземпляру, на котором работает данный метод?

ответ

1

Цель вызова метода - это экземпляр MyClass, но сам делегат не является вызовом метода. Это то, что будет выполнять вызов метода при его выполнении.

Если вы посмотрите на func.Target, вы увидите, что это System.Runtime.CompilerServices.ExecutionScope.

Теперь вы можете проверить это, наложить на него, а затем получить либо локали, либо глобальные (не уверенные), чтобы получить цель. Тем не менее, я подозреваю, что было бы чище просто изменить использовать Func<int> (или какой бы тип ваш код ошибки) и вернуть код ошибки при выполнении делегата в первую очередь. Тогда вам даже не понадобится дерево выражений.

EDIT: Учитывая ваши комментарии, я предлагаю:

public static void TryCommand(Expression<Func<MyClass,bool>> command, 
           MyClass c) 
{ 
    // Code as before to find the method name etc. 

    Func<MyClass, bool> compiled = command.Compile(); 

    if (!compiled(c)) 
    { 
     Console.WriteLine(methodCallExpression.Method.Name 
      + "() failed with error code " + c.GetError()); 
    } 
} 

Вы бы затем вызвать его:

TryCommand(x => x.SomeMethod(), myClass); 
+0

Джон, я работаю с третьей стороной "MyClass" (извините, плохо именование на моем part!), поэтому я не могу изменить его на Func [int]. Я рассмотрю ваше первое предложение. Я согласен, что это не кажется чистым, но в основном я просто узнаю о том, что возможно. Спасибо за предложения! – Akash 2008-12-04 13:40:30

+0

Как вы вызываете TryCommand? У вас есть контроль над этим? Вы можете написать метод-обертку, который принимает Func и MyClass, а затем вызывает делегата и при необходимости проверяет код ошибки. – 2008-12-04 14:04:28

1

Цель равна нулю, так как метод является статическим. В отражении Invoke (..) на статическом MethodInfo игнорирует цель. Вероятно, это метод расширения, и в этом случае первым аргументом является предполагаемая цель.

Поскольку большинство LINQ основано на методах расширения, вы увидите это довольно часто с отражением.

3

Акаш, как только у вас есть MethodCallExpression, вы можете восстановить вызывающего метода. Вы должны восстановить MemberExpression и построить дерево выражений, которое оценивает его.

Смотрите код ниже:


MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body; 
MemberExpression memberExpression = (MemberExpression)methodCallExpression.Object; 

Expression<Func<Object>> getCallerExpression = Expression<Func<Object>>.Lambda<Func<Object>>(memberExpression); 
Func<Object> getCaller = getCallerExpression.Compile(); 
MyClass caller = (MyClass)getCaller(); 
 

Надеется, что это помогает,

Ricardo Lacerda Каштел Бранке

Смежные вопросы