Update: This does work, I was being stupid :(
i have the following extension method
public static string ExtMethod(this object self, object myparameter);
at runtime this is called in any number of ways ways, i think these are all possibilities:
Expression<Func<T, string>> expr = x => x.property.ExtMethod(5);
Expression<Func<T, string>> expr = x => x.property.ExtMethod(new object());
Expression<Func<T, string>> expr = x => x.property.ExtMethod(someMethod());
Expression<Func<T, string>> expr = x => x.property.ExtMethod(x.someMethod());
Expression<Func<T, string>> expr = x => x.property.ExtMethod(x.OtherProperty);
what i need to do is evaluate the "myparameter", given "expr" and a "T"
because of the two cases where x is used in myparameter, i thought i needed to create a delegate of the form:
Expression<Func<T, object>> expr = x => [myparameter expression here]
i thought this would work:
var extMethodExpr = expr.Body as MethodCallExpression;
var myparameterExpr = extMethodExpr.Arguments[1];
var myparam = Expression.Lambda(myparameterExpr, expr.Parameters).Compile().Invoke(someT)
but for the expressions that do not involve x, (削除) i get TargetParameterCountException :( (削除ここまで)
in these cases, if i do:
var myparam = Expression.Lambda(myparameterExpr).Compile().Invoke(someT)
it works fine.
How do I solve this?
thanks
-
This is hardcore... Still can't understand what is the problem. :)Arnis Lapsa– Arnis Lapsa2009年05月24日 11:23:06 +00:00Commented May 24, 2009 at 11:23
-
Extension methods on object are rarely a good idea; and (pedant) you're creating an expression tree (not a delegate) - but looking now...Marc Gravell– Marc Gravell2009年05月24日 11:36:20 +00:00Commented May 24, 2009 at 11:36
-
@Marc this is just pseudo code ;)Andrew Bullock– Andrew Bullock2009年05月24日 11:43:43 +00:00Commented May 24, 2009 at 11:43
-
For info; you can see more complete parameter evaluation code in protobuf-net; look at ResolevMethod, and how it evaluates the args: code.google.com/p/protobuf-net/source/browse/trunk/…Marc Gravell– Marc Gravell2009年05月24日 12:01:23 +00:00Commented May 24, 2009 at 12:01
-
@arnis, see my update, thats probably why you cant find a problem!Andrew Bullock– Andrew Bullock2009年05月24日 15:26:46 +00:00Commented May 24, 2009 at 15:26
2 Answers 2
OK; got to the bottom of it; in the line:
var myparam = Expression.Lambda(myparameterExpr).Compile().Invoke(someT);
If you weren't trying to pass in a someT, this would work for those expressions that don't involve x in the argument; for those that do, you need to tell the lambda to include the parameter (the same one from the original lambda) - simply by:
var myparam = Expression.Lambda(myparameterExpr,
outerLambda.Parameters[0]).Compile().Invoke(someT);
Here's some working code that evaluates the inner parameter (given an instance of the argument type); note that I use the parameter even if it doesn't involve an x - otherwise, what would it do with the instance?
using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo {
public string Bar {get;set;}
public int someMethod() { return 4; }
public int OtherProperty { get { return 3; } }
}
static class Program
{
static int someMethod() { return 3; }
static void Main()
{
Foo foo = new Foo();
Test<Foo>(x => x.Bar.ExtMethod(5), foo);
Test<Foo>(x => x.Bar.ExtMethod(new object()), foo);
Test<Foo>(x => x.Bar.ExtMethod(someMethod()), foo);
Test<Foo>(x => x.Bar.ExtMethod(x.someMethod()), foo);
Test<Foo>(x => x.Bar.ExtMethod(x.OtherProperty), foo);
}
static void Test<T>(Expression<Func<T, string>> expr, T instance)
{
if (expr.Body.NodeType != ExpressionType.Call)
{
throw new InvalidOperationException("Call expected");
}
var call = ((MethodCallExpression)expr.Body);
if (call.Method != typeof(Program).GetMethod(
"ExtMethod", BindingFlags.Static | BindingFlags.NonPublic))
{
throw new InvalidOperationException("ExtMethod expected");
}
// we know that ExtMethod has 2 args; pick myParameter (the 2nd);
// then build an expression over arg, re-using our outer param
var newLambda = Expression.Lambda<Func<T, object>>(
call.Arguments[1], expr.Parameters[0]);
// evaluate it and show the argument value
object value = newLambda.Compile()(instance);
Console.WriteLine(value);
}
static string ExtMethod(this object self, object myParameter) {
return self.ToString();
}
}
2 Comments
What if you check expr.Parameters.Count and if it's 0, invoke without the parameters?