получить конечные значения из параметров метода лямбда-выражений

в основном я хочу получить значения параметров вызываемого метода следующим образом:

var x = 1;
var a = 2;
var b = 3;
Do<HomeController>(o => o.Save(x, "Jimmy", a+b+5, Math.Sqrt(81)));

public static void Do<T>(Expression<Action<T>> expression) where T : Controller
{
  // get the values 1,Jimmy,10,9 here
}

Ответы на вопрос(5)

вы можете проверить, является ли выражение выражением 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)
    {
    }
}
 Omu22 сент. 2010 г., 09:23
Я отредактировал свой вопрос, не могли бы вы взглянуть
 Rohan West22 сент. 2010 г., 08:20
@Jon, у меня не было времени, чтобы проверить ошибки :(
 Jon Skeet22 сент. 2010 г., 08:27
Да, в любом случае это довольно хрупко. Я сомневаюсь, что это лучший подход, но у нас не так много контекста.
 Jon Skeet22 сент. 2010 г., 08:18
Мне нравится тот факт, что наш демонстрационный код практически идентичен :)

что это поможет вам и кому-то еще.

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;
}

предназначенный для работы с любым выражением - в том смысле, что он не предполагает, что вы передаете выражение вызова метода. Тем не менее, это не завершено. Вам придется заполнить все остальное.

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();
}

Для случая, который вы упомянули, это уже работает:

// 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);

Для более сложных лямбда-выражений, которые используют другие типы узлов выражений, вам придется постепенно расширять приведенный выше код. Каждый раз, когда вы используете его, и он бросаетNotImplementedExceptionвот что я делаю:

Откройте окно Watch в отладчикеПосмотри наexpression переменная и ее типДобавьте необходимый код для обработки этого типа выражения

Со временем метод будет становиться все более и более полным.

Решение Вопроса

MethodCallExpression, а затем посмотрите на аргументы к нему. Обратите внимание, что у нас нет значенияoТаким образом, мы должны предположить, что аргументы метода не полагаются на это. Также мы по-прежнему предполагаем, что лямбда-выражение просто полагается наMethodCallExpression?

РЕДАКТИРОВАТЬ: Хорошо, вот отредактированная версия, которая оценивает аргументы. Тем не менее, предполагается, что выне действительно используя параметр лямбда-выражения в аргументах (что являетсяnew object[1] о - это обеспечивает нулевой параметр, эффективно).

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);
        }
    }
}
 Jon Skeet22 сент. 2010 г., 08:56
@Omu: Это проблема с простым сбросом кода в вопрос без объяснения причин. я сделалсказать это было хрупко, и потерпело неудачу, как только вы начали использовать любой другой шаблон ...
 Omu22 сент. 2010 г., 09:00
@Jon Skeet Я просто стараюсь сделать вопросы максимально простыми
 Adam Houldsworth23 сент. 2014 г., 16:43
То, что мне разрешено делать подобные вещи, пугает меня немного. Мы только что использовали это для прозрачного способа создания ключей Memoiser.
 Omu22 сент. 2010 г., 09:09
@ Джон Скит, я редактировал свой вопрос, пожалуйста, посмотрите
 Jon Skeet22 сент. 2010 г., 09:25
@ Ому: я отредактировал свой ответ, чтобы соответствовать вопросу. Я думаю, что он делает то, что ты хочешь, хотя я все еще сказал бы, что это довольно хрупко.
 Omu22 сент. 2010 г., 09:36
@ Джон Скит: большое спасибо, это то, что я хотел
 Jon Skeet22 сент. 2010 г., 08:13
@ Ому: Но вы действительно хотите код, который сломался бы - только во время выполнения - если вы использовалилюбой другая форма лямбда-выражения? Если вывсегда будет вызывать Save, почему бы просто не передать параметры напрямую?
 Omu22 сент. 2010 г., 08:35
@ Джон Скит, я собираюсь вызывать множество других методов с другими параметрами, я не знал, что могу вызывать только с постоянными значениями, я думаю, мне придется исследовать это дальше
  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();
        }

Ваш ответ на вопрос