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();
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