basically I want to get the values of the parameters of a called method like this:
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
}
Well, you'd need to drill into the expression, find the MethodCallExpression
, and then look at the arguments to it. Note that we don't have the value of o
, so we've got to assume that the arguments to the method don't rely on that. Also we're still assuming that the lambda expression just relies on it being a MethodCallExpression
?
EDIT: Okay, here's an edited version which evaluates the arguments. However, it assumes you're not really using the lambda expression parameter within the arguments (which is what the new object[1]
is about - it's providing a null parameter, effectively).
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);
}
}
}
As Jon said you can check to see if the expression is a 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)
{
}
}
My universal answer is below. I hope it will help you and somebody else.
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;
}
Here is some code that is designed to work with any expression — in the sense that it doesn’t fundamentally assume that you are passing in a method-call expression. However, it is not complete. You will have to fill in the rest.
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();
}
For the case that you have mentioned, this already works:
// 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);
For more complex lambda expressions that employ other types of expression nodes, you will have to incrementally extend the above code. Every time you use it and it throws a NotImplementedException
, here is what I do:
expression
variable and its typeOver time the method will become more and more complete.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With