The Following Code tries to Create a Func delegate that converts from the original func Argument Type to another type.
public static Delegate Convert<T1, R>(this Func<T1, R> func, Type argType)
{
var param = Expression.Parameter(argType);
var convertedParam = new Expression[] { Expression.Convert(param, typeof(T1))};
var call = Expression.Convert(
func.Target == null || func.Target is Closure
? Expression.Call(func.Method, Expression.Constant(func.Target), convertedParam[0])// this path causes the error
: Expression.Call(Expression.Constant(func.Target), func.Method, convertedParam), typeof(R));
var delegateType = typeof(Func<,>).MakeGenericType(argType, typeof(R));
var lambda = Expression.Lambda(delegateType, call, param);
return lambda.Compile();// BUG: 'MethodInfo must be a runtime MethodInfo object.
}
My problem starts when the Func contains a closure as Target, lambda.Compile() bugs saying "Method Info must be a runtime MethodInfo Object" i suspect it's because the method is static.
Can someone please explain to me what i am doing wrong? and why? i obviously don't understand Expressions well enough to fix this on my own.
Thanks in advance.
You should call Expression.Invoke, which will call a delegate directly.
Pass it Expression.Constant(func).
Related
Let's say I have an object of a certain class A.
Let's say I need an Expression which calls method M on class A.
Is this even doable?
Basically, I need to programatically get this lambda
a => a.M();
The trick is, I want to do this generically, i.e. I plan to use reflection to figure out that the method is called M and what parameters it wants.
I tried using Expression.Lambda(MethodCallExpression method, ParameterExpression params).
The issue there is that when I define the method call expression, I have to specify an instance (or leave it at null if it's a static method, which it isn't). I don't want this. I want whatever the parameter is passed into the lambda (a in my example) to be the instance.
Is this possible?
Yes, it's possible to construct a linq expression at a runtime.
E.g. below is an example of constructing an expression of a method call which returns an object. This is really a dummy example as it's better to avoid object in favor of strict types.
static Expression<Func<T, object>> ComposeMethodCallExpressionAsFuncObject<T>(string methodName)
{
MethodInfo mi = typeof(T).GetMethod(methodName, types: new Type[0])
?? throw new ArgumentException($"There is no '{methodName}' method in the '{typeof(T).Name}' with the empty arguments list!");
var paramExpression = Expression.Parameter(typeof(T));
var methodCallExpression = Expression.Call(paramExpression, mi);
var result = Expression.Lambda<Func<T, object>>(methodCallExpression, paramExpression);
return result; // (T obj) =>obj.methodName()
}
, and example of usage:
int foo = 9988;
var expression = ComposeMethodCallExpressionAsFuncObject<int>(nameof(int.ToString));
//expression: (int obj) => obj.ToString()
var result = expression.Compile()(foo);
Assert.AreEqual("9988", result);
I have the method below which converts a (non-static) MethodInfo into a compiled Expression (Func) which I can then call.
This works great: I can call it with a method expecting both reference objects and value types.
BUT unlike the original method where I could call a method that had a parameter expecting a double and pass it an int this compiled expression doesn't support that and throws an InvalidCastException.
How do I modify this to support the same type of implicit casting that happens during a normal method call?
Bonus question: should the instanceExp use the DeclaringType or the ReflectedType from the MethodInfo?
public Func<object, object[], object> Create(MethodInfo methodInfo)
{
var methodParams = methodInfo.GetParameters();
var arrayParameter = Expression.Parameter(typeof(object[]), "array");
var arguments =
methodParams.Select((p, i) => Expression.Convert(
Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType))
.Cast<Expression>()
.ToList();
var instanceParameter = Expression.Parameter(typeof(object), "controller");
var instanceExp = Expression.Convert(instanceParameter, methodInfo.DeclaringType);
var callExpression = Expression.Call(instanceExp, methodInfo, arguments);
var bodyExpression = Expression.Convert(callExpression, typeof(object));
return Expression.Lambda<Func<object, object[], object>>(
bodyExpression, instanceParameter, arrayParameter)
.Compile();
}
--- EDIT
The working solution is:
var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(TypeCode) });
var arguments =
methodParams.Select((p, i) =>
!typeof(IConvertible).IsAssignableFrom(p.ParameterType)
// If NOT IConvertible, don't try to convert it
? (Expression)Expression.Convert(
Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType)
:
// Otherwise add an explicit conversion to the correct type to handle int <--> double etc.
(Expression)Expression.Convert(
Expression.Call(changeTypeMethod,
Expression.ArrayAccess(arrayParameter, Expression.Constant(i)),
Expression.Constant(Type.GetTypeCode(p.ParameterType))),
p.ParameterType)
)
.ToList();
The problem is the same as in this piece of C# code:
object a = 123;
double b = (double)a; // InvalidCastException
The reason is that a is an object, so in order to make it a double the cast must unwrap it, and then transform an int to double. The language allows the cast to do only one thing - it will either unwrap or transform, but not both. You need to tell the compiler how to do this cast explicitly by telling it that there is an int wrapped inside the object:
double b = (double)((int)a); // Works
If you could do the same thing in your LINQ expression, your compiled expression will work as well. However, you may not know the actual type of the parameter at the time you generate your expression, so you may want to go for a different strategy - wire in a call to Convert.ChangeType method, which can unwrap and cast at the same time.
My journey to this question started with an implementation of Jon Skeet's article: "Making reflection fly and exploring delegates":
and in it, he states:
Note: I was going to demonstrate this by calling DateTime.AddDays, but for value type instance methods the implicit first first parameter is passed by reference, so we’d need a delegate type with a signature of DateTime Foo(ref DateTime original, double days) to call CreateDelegate. It’s feasible, but a bit of a faff. In particular, you can’t use Func<...> as that doesn’t have any by-reference parameters.
I am using my own delegates in an effort to support this and I am just totally stumped trying to create compiled expressions in C# .NET 4.0 to support "out / ref" but converting the delegate types to "object".
When I run the code below, I get the expected output in the first case, however, in the second case (when I convert the input and output parameters to type: object) the "out" parameter is not assigned and the result is "before" as opposed to "after".
public class Test
{
public delegate void myDelegate<T, U>(T test, out U setMe);
public void myFunction(out string setMe)
{
setMe = "after";
}
}
private static playin.program.Test.myDelegate<Test, string> buildExactExpression(MethodInfo methodInfo)
{
ParameterExpression instance = Expression.Parameter(typeof(Test));
ParameterExpression argument = Expression.Parameter(typeof(string).MakeByRefType());
var methodCall = Expression.Call(
instance,
methodInfo,
argument);
return Expression.Lambda<playin.program.Test.myDelegate<Test, string>>(methodCall, new ParameterExpression[] { instance, argument }).Compile();
}
private static playin.program.Test.myDelegate<object, object> buildDesiredExpression(MethodInfo methodInfo)
{
ParameterExpression instance = Expression.Parameter(typeof(object));
ParameterExpression argument = Expression.Parameter(typeof(object).MakeByRefType());
var methodCall = Expression.Call(
Expression.Convert(instance, typeof(Test)),
methodInfo,
Expression.Convert(argument, typeof(string)));
return Expression.Lambda<playin.program.Test.myDelegate<object, object>>(methodCall, new ParameterExpression[] { instance, argument }).Compile();
}
static void Main(string[] args)
{
Test t1 = new Test();
var myFunctionMethodInfo = t1.GetType().GetMethod("myFunction");
//this one works, the "out" string is set to "after"
string someString = "before";
var compiledExactly = buildExactExpression(myFunctionMethodInfo);
compiledExactly(t1, out someString);
Console.WriteLine(someString);
//the following doesn't return the expected output, the "out" parameter is not set, "before" is printed
object someObjectString = "before";
var compiledObject = buildDesiredExpression(myFunctionMethodInfo);
compiledObject(t1, out someObjectString);
Console.WriteLine(someObjectString);
}
For some objects in my prog, I am discovering their methods at runtime and do not know the parameter types ahead of time, so the conversion to "object" (buildDesiredExpression method) in the delegate that is returned is v.important. I would like to cache the returned "open" delegates so that I can run them with a minimal performance penalty during execution.
How can I fix the "buildDesiredExpression" method to make this work?
What you can do is to create a local variable of the right type, let the called method set that, and then set the parameter:
ParameterExpression instance = Expression.Parameter(typeof(object));
ParameterExpression argument =
Expression.Parameter(typeof(object).MakeByRefType());
ParameterExpression argumentVariable = Expression.Parameter(typeof(string));
var methodCall = Expression.Call(
Expression.Convert(instance, typeof(Test)),
methodInfo,
argumentVariable);
var block = Expression.Block(
new[] { argumentVariable },
methodCall, Expression.Assign(argument, argumentVariable));
return Expression.Lambda<Test.myDelegate<object, object>>(
block, new[] { instance, argument }).Compile();
In C# I am attempting to use linq expressions to generate calls to certain methods. One of the parameters to the method is a delegate. I have the MethodInfo for the method I want to pass as a delegate I just am not sure of the linq syntax for creating delegates.
This is a bit contrived but I hope this shows what I'm trying to do:
[C#]
delegate void Example();
object instance = ...;
MethodInfo methodToCall = ...;
MethodInfo methodToReference = instance.GetType().GetMethod("Foo");
var lambda = Expression.Call(
methodToCall,
Expression.New(
typeof(Example).GetConstructor(new [] { typeof(object), IntPtr }),
Expression.Constant(instance),
Expression.Constant(/* IntPtr from MethodInfo?? */)));
lambda.Compile()();
The problem is that the constructor for a delegate is asking for an IntPtr, I am not sure how to get that! Is there a more direct way to create a delegate object than trying to use the New() expression method?
Example e = (Example)Delegate.CreateDelegate(typeof(Example), instance, methodToReference);
You can use Lambda Expression Objects to represent a lambda as an expression.
How do you create a Lambda Expression Object representing a generic method call, if you only know the type -that you use for the generic method signature- at runtime?
For example:
I want to create a Lambda Expression Objects to call:
public static TSource Last<TSource>( this IEnumerable<TSource> source )
But I only know what TSource is at runtime.
static Expression<Func<IEnumerable<T>, T>> CreateLambda<T>()
{
var source = Expression.Parameter(
typeof(IEnumerable<T>), "source");
var call = Expression.Call(
typeof(Enumerable), "Last", new Type[] { typeof(T) }, source);
return Expression.Lambda<Func<IEnumerable<T>, T>>(call, source)
}
or
static LambdaExpression CreateLambda(Type type)
{
var source = Expression.Parameter(
typeof(IEnumerable<>).MakeGenericType(type), "source");
var call = Expression.Call(
typeof(Enumerable), "Last", new Type[] { type }, source);
return Expression.Lambda(call, source)
}
I don't fully understand the question, but the code that dtb wrote could be written simply as:
class MyUtils {
public static Expression<Func<IEnumerable<T>, T>> CreateLambda<T>() {
return source => source.Last();
}
}
The code in the sample by dtb is pretty much the same thing as what the C# compiler automatically generates for you from this lambda expression (compiled as expression tree, because the return type is Expression).
If you know the type at runtime, then you can either use the solution by dtb or you can invoke the CreateLambda method (above) using Reflection, which may be slower, but allows you to write the code in the lambda in the natural C#:
var createMeth = typeof(MyUtils).GetMethod("CreateLambda");
LambdaExpression l = createMeth.MakeGenericMethod(yourType).Invoke();
The nice thing about this approach is that the code in CreateLambda can be much more complicated, which would be really hard to do using expression trees explicitly.