I have a delegate that I have created in runtime because I don't know its parameter types in design time. The code is the following:
var delegMethodType = typeof(Func<,,,>).MakeGenericType(type1, type2, type3, returnType);
As you can see I'm using Func<,,,> to represent Func<T1, T2, T3, TReturn>. It works perfectly.
After that, I create my delegate in this way:
var deleg = Delegate.CreateDelegate(delegMethodType, obj, methodInfo);
Where "delegMethodType" is the type for the delegate that I created before, "obj" is the object that receives the call, and "methodInfo" is the reflection method for "obj". This works perfectly.
Now, I need to call the delegate but due to "Delegate.CreateDelegate" returns a basic delegate, I cannot call it with the parameters:
// due to this delegate was created in runtime VS.NET doesn't recognize
// that the delegate allows three parameters and returns a value
var result = deleg(val1, val2, val3);
I can use the delegate with "deleg.DynamicInvoke()" but it's slow than calling the delegate with its parameters.
How I can call this delegate with three parameters and get its return value?
It's possible to just invoke the methodInfo directly, or somewhat more complicatedly use the Linq Expressions library to create a Func<object,object,...> that can be invoked that does all of the required casts.
Note that I am assuming that you do know the number of parameters at compile time, as your question indicates with the Func<,,,> usage.
It's a bit involved, but basically comes down to using Expression.Lambda(...).Compile() to create the final func that will be invoked, Expression.Convert() to cast from objects to the actual types so that Expression.Call() can be used to call the method on objects with the correct types from our object Expression.Parameters
This example shows how to implement both approaches.
using System;
using System.Linq.Expressions;
namespace SO
{
class SO69847110
{
public TR fn<T1,T2,T3,TR>(T1 p1, T2 p2, T3 p3)
{
if(typeof(TR) == typeof(string))
{
return (TR)(object)"Hello World";
}
return default(TR);
}
static void Main(string[] args)
{
var returnType = typeof(string);
var paramType = typeof(int);
var delegMethodType = typeof(Func<,,,>).
MakeGenericType(
paramType,
paramType,
paramType,
returnType
);
var methodInfo = typeof(SO69847110).GetMethod("fn")
.MakeGenericMethod(
paramType,
paramType,
paramType,
returnType
);
var obj = new SO69847110();
//var deleg = Delegate.CreateDelegate(delegMethodType, obj, methodInfo);
var methodInfoInvoke = methodInfo.Invoke(obj, new object[] { 0, 1, 2 });
Console.WriteLine(methodInfoInvoke);
var param1 = Expression.Parameter(typeof(object));
var param2 = Expression.Parameter(typeof(object));
var param3 = Expression.Parameter(typeof(object));
var convertedParameterExpressions =
new Expression[]
{
Expression.Convert(param1,paramType),
Expression.Convert(param2,paramType),
Expression.Convert(param3,paramType),
};
var expressed = Expression.Lambda<Func<object, object, object, object>>(
Expression.Call(
Expression.Convert(Expression.Constant(obj), obj.GetType()),
methodInfo,
convertedParameterExpressions
),
tailCall:false,
parameters: new[]
{
param1,
param2,
param3
}
).Compile();
var expressedInvoked = expressed.Invoke(obj, 0, 1, 2);
Console.WriteLine(expressedInvoked);
}
}
}
If you want to call the same function for multiple objects, then you can also make the subject a Func parameter too - just make it a separate Expression.Parameter(typeof(object)) at the start, include that parameter instead of the Expression.Constant and also include in the list of parameters for Expression.Lambda. Then also pass in the subject as a parameter when invoking.
I am trying to construct function that accepts more that one parameter. Lambda function accepting one parameter works fine. Here is the code.
var value = 22.55;
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { value.GetType() });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
var block = Expression.Block(
inputParameter,
Expression.Call(inputParameter, method, Expression.Constant(value)));
var function = Expression.Lambda<Action<TextWriter>>(block, inputParameter).Compile();
function(Console.Out);
However when I add one more parameter that I do not even use then function throws null reference exception. I must be missing something but have no idea what. Here is the code that does not work:
var value = 22.55;
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { value.GetType() });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
// Additional parameter here
ParameterExpression inputParameter2 = Expression.Parameter(typeof(double));
var block = Expression.Block(
// Function block accepts two parameters
new List<ParameterExpression>() { inputParameter, inputParameter2 },
Expression.Call(inputParameter, method, Expression.Constant(value)));
var function = Expression.Lambda<Action<TextWriter, double>>(block, inputParameter, inputParameter2).Compile();
// ....aaand null exception here. Why?
function(Console.Out, value);
What am I doing wrong here?
Judging by your comment, you're misunderstanding what you're passing to Block. The variables argument of Block is to define local variables, rather than parameters.
var block = Expression.Block(
// Function block accepts two parameters
new List<ParameterExpression>() { inputParameter, inputParameter2 },
Expression.Call(inputParameter, method, Expression.Constant(value)));
Should be simply
var block = Expression.Block(Expression.Call(inputParameter, method, Expression.Constant(value)));
What you're currently doing is equivelant to:
void function(TextWriter tw, double d) {
TextWriter tw = default(TextWriter);
double d = default(double);
tw.WriteLine(22.55);
}
While you actually just want:
void function(TextWriter tw, double d) {
tw.WriteLine(22.55);
}
Also, you're not actually using your second parameter. The complete working code is:
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { typeof(double) });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
ParameterExpression inputParameter2 = Expression.Parameter(typeof(double));
var block = Expression.Block(Expression.Call(inputParameter, method, inputParameter2));
var function = Expression.Lambda<Action<TextWriter, double>>(block, inputParameter, inputParameter2).Compile();
function(Console.Out, 35.5);
Your first code worked because you used a different overload of block. Instead of declaring a local variable for inputParameter, you passed it as an expression itself. So, the code looked like this:
void function(TextWriter tw, double d) {
tw; //Illegal for the C# compiler, but not illegal as an expression. This will do nothing.
tw.WriteLine(22.55);
}
I have seen this Issue while building dynamic Expression Tree and Expression/Statement trees and since I am new to expression trees I am still struggling to understand how to achieve what I want.
A contrived object is below
public class TestObject
{
public TestObject()
{
ClassList = new List<Class>();
}
public int Age { get; set; }
public List<Class> ClassList { get; set; }
}
public class Class
{
public string Name { get; set; }
public int ClassId { get; set; }
}
At run time I iterate through each of the properties and generate a Delegate which does a conversion to string of that property. I have got all that working. The issue I have to deal with now is that for the List type, I need to be able to apply a set of actions to each item in the ClassList property so I need a foreach which allows me to do that.
I currently have this
//type==TestObject at runtime
//propertyName == "ClassList"
ParameterExpression recordExpression = Expression.Parameter(type, "record");
memberExpression = MemberExpression.Property(recordExpression, propertyName);
Type getEnumerableDelegateType =
typeof(Func<,>).MakeGenericType(new Type[] { type, memberExpression.Type});
var getList = Expression.Lambda(getEnumerableDelegateType, memberExpression, recordExpression);
GetList when compiled and invoked returns the List as expected. What I m struggling with is how to create an expression which will use the result from the lambda expression and iterate over it applying the set of actions I have already created for each Class item.
Ultimately I am looking for a lambda signature to match the overallAction signature below
var getListFunc = new Func<TestObject, List<Class>>((TestObject obj1) => obj1.ClassList);
Action<List<Class>> listAction = delegate(List<Class> data)
{
foreach (var dataChannelWithUnitse in data)
{
//Apply generated delegate
}
};
Action<TestObject> overallAction = delegate(TestObject data)
{
var x = getListFunc.Invoke(data);
listAction.Invoke(x as List<Class>);
};
Any help is appreciated to help me understand how to do this.
I have currently got this which is exceptioning with variable 'Input' of type 'TestObject' referenced from scope '', but it is not defined
var typeParam = Expression.Parameter(type, "Input");
var listVariable = Expression.Variable(memberExpression.Type, "List");
var enumerator = Expression.Variable(typeof(IEnumerator<>).MakeGenericType(dataType));
var enumeratorType = typeof(IEnumerator<>).MakeGenericType(dataType);
var enumerableType = typeof(IEnumerable<>).MakeGenericType(dataType);
var enumerableParam = Expression.Parameter(enumerableType, "ExtractedCollection");
var getEnumeratorFunc = Expression.Call(enumerableParam, enumerableType.GetMethod("GetEnumerator"));
var getEnumeratorLambda = Expression.Lambda(getEnumeratorFunc, enumerableParam);
var t1 = Expression.Assign(listVariable, Expression.Invoke(getListLambda, typeParam));
var t2 = Expression.Assign(enumerator, Expression.Invoke(getEnumeratorLambda, listVariable));
var #break = Expression.Label();
var funcBlock = Expression.Block(
new ParameterExpression[] { listVariable, enumerator},
t1,
t2,
Expression.Loop(
Expression.IfThenElse(
Expression.NotEqual(Expression.Call(enumerator,typeof(IEnumerator).GetMethod("MoveNext")),Expression.Constant(false)),
Expression.Invoke(enumerableExpressions[0],Expression.Property(enumerator, "Current")),
Expression.Break(#break))
, #break), typeParam);
Expression<Action<TestObject>> lm = Expression.Lambda<Action<TestObject>>(funcBlock,recordExpression);
var d = lm.Compile(); **//this is exceptioning with " variable 'Input' of type 'TestObject' referenced from scope '', but it is not defined**
I got lost somewhere in the middle of your question (and if I've interpreted it incorrectly, please tell me, and I'll dive back into it), but I think this is what you're after:
public static Expression ForEach(Expression collection, ParameterExpression loopVar, Expression loopContent)
{
var elementType = loopVar.Type;
var enumerableType = typeof(IEnumerable<>).MakeGenericType(elementType);
var enumeratorType = typeof(IEnumerator<>).MakeGenericType(elementType);
var enumeratorVar = Expression.Variable(enumeratorType, "enumerator");
var getEnumeratorCall = Expression.Call(collection, enumerableType.GetMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
// The MoveNext method's actually on IEnumerator, not IEnumerator<T>
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("LoopBreak");
var loop = Expression.Block(new[] { enumeratorVar },
enumeratorAssign,
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, Expression.Constant(true)),
Expression.Block(new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
loopContent
),
Expression.Break(breakLabel)
),
breakLabel)
);
return loop;
}
To use it, you need to supply a collection to iterate over, an expression to substitute into the body of the loop, and a ParameterExpression which is used by the loop body expression, which will be assigned to the loop variable on each loop iteration.
I think sometimes examples speak louder than words...
var collection = Expression.Parameter(typeof(List<string>), "collection");
var loopVar = Expression.Parameter(typeof(string), "loopVar");
var loopBody = Expression.Call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }), loopVar);
var loop = ForEach(collection, loopVar, loopBody);
var compiled = Expression.Lambda<Action<List<string>>>(loop, collection).Compile();
compiled(new List<string>() { "a", "b", "c" });
EDIT: As Jeroem Mostert correctly points out in the comments, this doesn't quite mirror the "real" behaviour of a foreach loop: this would make sure that it disposes the enumerator. (It would also create a new instance of the loop variable for each iteration, but that doesn't make sense with expressions). Implementing this is just a matter of turning the handle if you feel motivated enough!
For anyone watching at home, I've got a similar method for generating 'for' loops:
public static Expression For(ParameterExpression loopVar, Expression initValue, Expression condition, Expression increment, Expression loopContent)
{
var initAssign = Expression.Assign(loopVar, initValue);
var breakLabel = Expression.Label("LoopBreak");
var loop = Expression.Block(new[] { loopVar },
initAssign,
Expression.Loop(
Expression.IfThenElse(
condition,
Expression.Block(
loopContent,
increment
),
Expression.Break(breakLabel)
),
breakLabel)
);
return loop;
}
This is equivalent to the following statement, where the pseudo-variables match the Expressions in the method above:
for (loopVar = initValue; condition; increment)
{
loopContent
}
Again, loopContent, condition, and increment are Expressions which uses loopVar, and loopVar is assigned on every iteration.
relatively_random's solution is great but foreach handles several other scenarios. Check these links to SharpLab to verify what is generated in each of them:
When the enumerable is a IEnumerable<T>, it checks if the enumerator is null before calling Dispose().
When the enumerable is not an interface, the enumerator changes to have the type returned by GetEnumerator(). The enumerator is cast to IDisposable before calling Dispose().
When the enumerator doesn't implement IDisposable, as is used on the check if the enumerator implements IDisposable. (???)
When the enumerator is a value type, the check for null goes away.
When the enumerator is a value type and doesn't implement IDisposable, the try/finally goes away.
The use of the type returned by GetEnumerator() is very important so that value type enumerators are not boxed. All the collections in System.Collections.Generic have value type enumerator because calls to its methods are not virtual, resulting in a lot better performance.
Putting all together results in the following code:
static partial class ExpressionEx
{
public static Expression ForEach<TSource>(Expression enumerable, Expression loopContent)
{
var enumerableType = enumerable.Type;
var getEnumerator = enumerableType.GetMethod("GetEnumerator");
if (getEnumerator is null)
getEnumerator = typeof(IEnumerable<>).MakeGenericType(typeof(TSource)).GetMethod("GetEnumerator");
var enumeratorType = getEnumerator.ReturnType;
var enumerator = Expression.Variable(enumeratorType, "enumerator");
return Expression.Block(new[] { enumerator },
Expression.Assign(enumerator, Expression.Call(enumerable, getEnumerator)),
EnumerationLoop(enumerator, loopContent));
}
public static Expression ForEach<TSource>(Expression enumerable, ParameterExpression loopVar, Expression loopContent)
{
var enumerableType = enumerable.Type;
var getEnumerator = enumerableType.GetMethod("GetEnumerator");
if (getEnumerator is null)
getEnumerator = typeof(IEnumerable<>).MakeGenericType(typeof(TSource)).GetMethod("GetEnumerator");
var enumeratorType = getEnumerator.ReturnType;
var enumerator = Expression.Variable(enumeratorType, "enumerator");
return Expression.Block(new[] { enumerator },
Expression.Assign(enumerator, Expression.Call(enumerable, getEnumerator)),
EnumerationLoop(enumerator,
Expression.Block(new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumerator, "Current")),
loopContent)));
}
static Expression EnumerationLoop(ParameterExpression enumerator, Expression loopContent)
{
var loop = While(
Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext")),
loopContent);
var enumeratorType = enumerator.Type;
if (typeof(IDisposable).IsAssignableFrom(enumeratorType))
return Using(enumerator, loop);
if (!enumeratorType.IsValueType)
{
var disposable = Expression.Variable(typeof(IDisposable), "disposable");
return Expression.TryFinally(
loop,
Expression.Block(new[] { disposable },
Expression.Assign(disposable, Expression.TypeAs(enumerator, typeof(IDisposable))),
Expression.IfThen(
Expression.NotEqual(disposable, Expression.Constant(null)),
Expression.Call(disposable, typeof(IDisposable).GetMethod("Dispose")))));
}
return loop;
}
public static Expression Using(ParameterExpression variable, Expression content)
{
var variableType = variable.Type;
if (!typeof(IDisposable).IsAssignableFrom(variableType))
throw new Exception($"'{variableType.FullName}': type used in a using statement must be implicitly convertible to 'System.IDisposable'");
var getMethod = typeof(IDisposable).GetMethod("Dispose");
if (variableType.IsValueType)
{
return Expression.TryFinally(
content,
Expression.Call(Expression.Convert(variable, typeof(IDisposable)), getMethod));
}
if (variableType.IsInterface)
{
return Expression.TryFinally(
content,
Expression.IfThen(
Expression.NotEqual(variable, Expression.Constant(null)),
Expression.Call(variable, getMethod)));
}
return Expression.TryFinally(
content,
Expression.IfThen(
Expression.NotEqual(variable, Expression.Constant(null)),
Expression.Call(Expression.Convert(variable, typeof(IDisposable)), getMethod)));
}
public static Expression While(Expression loopCondition, Expression loopContent)
{
var breakLabel = Expression.Label();
return Expression.Loop(
Expression.IfThenElse(
loopCondition,
loopContent,
Expression.Break(breakLabel)),
breakLabel);
}
}
The ForEach without loopVar is useful to enumerate without getting the items. That's the case of Count() implementation.
EDIT: An updated and tested version is available in the NetFabric.Reflection NuGet package. Check its repository for the source code.
Here's a slightly expanded version of canton7's excellent solution, taking into account the remarks about disposing the enumerator:
public static Expression ForEach(Expression enumerable, ParameterExpression loopVar, Expression loopContent)
{
var elementType = loopVar.Type;
var enumerableType = typeof(IEnumerable<>).MakeGenericType(elementType);
var enumeratorType = typeof(IEnumerator<>).MakeGenericType(elementType);
var enumeratorVar = Expression.Variable(enumeratorType, "enumerator");
var getEnumeratorCall = Expression.Call(enumerable, enumerableType.GetMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
var enumeratorDispose = Expression.Call(enumeratorVar, typeof(IDisposable).GetMethod("Dispose"));
// The MoveNext method's actually on IEnumerator, not IEnumerator<T>
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("LoopBreak");
var trueConstant = Expression.Constant(true);
var loop =
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, trueConstant),
Expression.Block(
new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
loopContent),
Expression.Break(breakLabel)),
breakLabel);
var tryFinally =
Expression.TryFinally(
loop,
enumeratorDispose);
var body =
Expression.Block(
new[] { enumeratorVar },
enumeratorAssign,
tryFinally);
return body;
}
I try create delegate type using an Expression class, but when I try create delegate from instance of MethodInfo I've got an ArgumentException. I using .NET 4.0
Here code:
var method = /*...*/;
List<Type> tArgs = new List<Type> { method.ReturnType };
var mparams = method.GetParameters();
mparams.ToList().ForEach(p => tArgs.Add(p.ParameterType));
var delDecltype = Expression.GetDelegateType(tArgs.ToArray());
return Delegate.CreateDelegate(delDecltype, method);
P.S. Sorry for my bad english;)
If you read the documentation for Expression.GetDelegateType(), you would see that the return type has to be the last argument.
That means this code should work:
var tArgs = new List<Type>();
foreach (var param in method.GetParameters())
tArgs.Add(param.ParameterType);
tArgs.Add(method.ReturnType);
var delDecltype = Expression.GetDelegateType(tArgs.ToArray());
return Delegate.CreateDelegate(delDecltype, method);
This code works for static methods only though. If you want create a delegate from instance method, you need to provide the instance you want to call the method on. To do that, change the last line to:
return Delegate.CreateDelegate(delDecltype, instance, method);