2010-09-22 16 views

Respuesta

17

Bueno, que había necesidad de perforar en la expresión, encontrar el MethodCallExpression , y luego mira los argumentos para ello. Tenga en cuenta que no tenemos el valor de o, por lo que debemos suponer que los argumentos del método no se basan en eso. También seguimos suponiendo que la expresión lambda solo se basa en que es un MethodCallExpression?

EDITAR: Bien, aquí hay una versión editada que evalúa los argumentos. Sin embargo, asume que está no realmente usando el parámetro de expresión lambda dentro de los argumentos (que es de lo que se trata el new object[1] - está proporcionando un parámetro nulo, efectivamente).

using System; 
using System.Linq.Expressions; 

class Foo 
{ 
    public void Save(int x, string y, int z, double d) 
    { 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     var x = 1; 
     var a = 2; 
     var b = 3; 
     ShowValues<Foo>(o => o.Save(x, "Jimmy", a + b + 5, Math.Sqrt(81))); 
    } 

    static void ShowValues<T>(Expression<Action<T>> expression) 
    { 
     var call = expression.Body as MethodCallExpression; 
     if (call == null) 
     { 
      throw new ArgumentException("Not a method call"); 
     } 
     foreach (Expression argument in call.Arguments) 
     { 
      LambdaExpression lambda = Expression.Lambda(argument, 
                 expression.Parameters); 
      Delegate d = lambda.Compile(); 
      object value = d.DynamicInvoke(new object[1]); 
      Console.WriteLine("Got value: {0}", value); 
     } 
    } 
} 
+2

@Omu: ¿Pero realmente quieres un código que se rompa, solo en el momento de la ejecución, si usaste * any * otra forma de expresión lambda? Si * siempre * va a llamar a Guardar, ¿por qué no pasa los parámetros directamente? –

+0

@Jon Skeet, voy a llamar a muchos otros métodos con diferentes parámetros, no sabía que solo puedo llamar con valores constantes, supongo que voy a tener que investigar esto más – Omu

+0

@Omu: Este es el problema con solo el código de dumping en una pregunta sin explicación. Dije * que era frágil y fallaría tan pronto como comenzaras a usar cualquier otro patrón ... –

4

Como Jon dijo que se puede comprobar para ver si la expresión es una MethodCallExpression

class Program 
{ 
    static void Main(string[] args) 
    { 
     Program.Do<Controller>(c => c.Save(1, "Jimmy")); 
    } 

    public static void Do<T>(Expression<Action<T>> expression) where T : Controller 
    { 
     var body = expression.Body as MethodCallExpression; 
     if (body != null) 
     { 
      foreach (var argument in body.Arguments) 
      { 
       var constant = argument as ConstantExpression; 
       if (constant != null) 
       { 
        Console.WriteLine(constant.Value); 
       } 
      } 
     } 
    } 
} 

public class Controller 
{ 
    public void Save(int id, string name) 
    { 
    } 
} 
+0

Me encanta el hecho de que nuestro código de demostración es casi idéntico :) –

+0

@Jon, No tuve tiempo de hacer ningún error al verificar tho :( –

+0

Sí, es bastante frágil de cualquier manera. Dudo que sea el mejor enfoque, pero no tengo mucho contexto. –

2

Aquí hay un código que está diseñado para trabajar con cualquier expresión - en el sentido de que no lo hace, fundamentalmente, supongamos que está pasando una expresión de llamada a método. Sin embargo, no está completo. Tendrás que completar el resto.

public static IEnumerable<object> ExtractConstants<T>(
     Expression<Action<T>> expression) 
{ 
    return extractConstants(expression); 
} 
private static IEnumerable<object> extractConstants(Expression expression) 
{ 
    if (expression == null) 
     yield break; 

    if (expression is ConstantExpression) 
     yield return ((ConstantExpression) expression).Value; 

    else if (expression is LambdaExpression) 
     foreach (var constant in extractConstants(
       ((LambdaExpression) expression).Body)) 
      yield return constant; 

    else if (expression is UnaryExpression) 
     foreach (var constant in extractConstants(
       ((UnaryExpression) expression).Operand)) 
      yield return constant; 

    else if (expression is MethodCallExpression) 
    { 
     foreach (var arg in ((MethodCallExpression) expression).Arguments) 
      foreach (var constant in extractConstants(arg)) 
       yield return constant; 
     foreach (var constant in extractConstants(
       ((MethodCallExpression) expression).Object)) 
      yield return constant; 
    } 

    else 
     throw new NotImplementedException(); 
} 

Para el caso de que usted ha mencionado, esto ya funciona:

// Prints: 
// Jimmy (System.String) 
// 1 (System.Int32) 
foreach (var constant in Ext.ExtractConstants<string>(
     str => Console.WriteLine("Jimmy", 1))) 
    Console.WriteLine("{0} ({1})", constant.ToString(), 
            constant.GetType().FullName); 

Para las expresiones lambda más complejas que emplean otros tipos de nodos de expresión, que tendrá que extender gradualmente el código de seguridad. Cada vez que lo utilice y se lanza una NotImplementedException, esto es lo que hago:

  • Abra la ventana de inspección en el depurador
  • mirada a la variable expression y su tipo
  • Añadir el código necesario para manejar ese tipo de expresión

Con el tiempo, el método será cada vez más completo.

2

Mi respuesta universal está abajo. Espero que te ayude a ti y a alguien más.

var dict = new Dictionary<string, object>(); 
var parameterExpressions = methodCallExpr.Arguments; 
foreach (var param in method.GetParameters()) 
{ 
    var parameterExpression = parameterExpressions[counter]; 
    var paramValueAccessor = Expression.Lambda(parameterExpression); 
    var paramValue = paramValueAccessor.Compile().DynamicInvoke(); 
    dict[param.Name] = paramValue; 
} 
0
public override IQueryable<Image> FindAll(System.Linq.Expressions.Expression<Func<Image, dynamic>> Id) 
     { 
      dynamic currentType = Id.Parameters[0]; 
      var id = currentType.Type.GUID; 
      var result = (_uniwOfWork as UnitOfWork).uspGetImages(id.ToString()); 
      return FindAll(); 
     } 

el uso de palabras clave dinámica.

Cuestiones relacionadas