I want to define a Lambda Expression with an out
parameter. Is it possible to do it?
Below are code snippets from a C# .Net 4.0 console app that I tried.
As you can see in Procedure25 I can use lambda expressions to define a delegate that has an output parameter, however, when I want to use linq expressions to do the same, the code in procedure 24 fails with:
System.ArgumentException was unhandled Message=ParameterExpression of type 'System.Boolean' cannot be used for delegate parameter of type 'System.Boolean&' Source=System.Core
I know I could use an input class object with a bool member and pass back the value to the caller that way but I was curious if I could somehow define out parameters.
Thanks
static void Main(string[] args)
{
Procedure25();
Procedure24();
Console.WriteLine("Done!");
Console.ReadKey();
}
private delegate int Evaluate(string value, out bool usesVars);
private static void Procedure24()
{
// This fails to compile:
//Expression<Evaluate> x = (string val, out bool usesSimVals) =>
//{
// usesSimVals = true;
// Console.WriteLine(val);
// return 1;
//};
ParameterExpression valueParameter = Expression.Parameter(typeof (string));
MethodCallExpression methodCall = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }), valueParameter);
bool usesVars;
ParameterExpression usesVarsParameter = Expression.Parameter(typeof (bool), "out usesVars");
Expression.Lambda<Evaluate>(methodCall, valueParameter, usesVarsParameter).Compile()("test", out usesVars);
Console.WriteLine(usesVars);
}
private static void Procedure25()
{
Evaluate x = (string value, out bool vars) => { vars = true;
Console.WriteLine(value);
return 1;
};
bool usesVars;
x("test", out usesVars);
}
EDIT:
Ani, awesome, thanks. So the key thing was to call MakeByRefType on the parameter type.
For the record here is a code snippet that works based on Ani's suggestion:
private static void Procedure24()
{
ParameterExpression valueParameter = Expression.Parameter(typeof (string));
MethodCallExpression methodCall = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }), valueParameter);
bool usesVars;
ParameterExpression usesVarsParameter = Expression.Parameter(typeof (bool).MakeByRefType(), "out usesVars");
Expression block = Expression.Block(methodCall, Expression.Assign(usesVarsParameter, Expression.Constant(true)), Expression.Constant(1));
int result = Expression.Lambda<Evaluate>(block, valueParameter, usesVarsParameter).Compile()("test", out usesVars);
Console.WriteLine("Result={0}, usesVars={1}", result, usesVars);
}
You need Type.MakeByRefType
:
var usesVarsParameter = Expression.Parameter(typeof(bool).MakeByRefType(), "usesVars");
Note that your code sample has an additional problem: your expression-body isn't correct - it's not returning a value when it should be returning an int
to satisfy the delegate-type's return-type.
Here's a way you can fix that (like your lambda example):
var body = Expression.Block(methodCall, Expression.Constant(1));
Expression.Lambda<Evaluate>(body, valueParameter, usesVarsParameter)
.Compile()("test", out usesVars);
Also note that you are not assigning the out
parameter inside the expression. Expression.Lambda
is letting you get away with it, which I didn't expect, but hey, the BCL doesn't have to follow the same rules as C#!
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