Exception when building Expression to call StringBuilder.Append(Object) with DateTime - c#

I've built a ToStringBuilder (found here) which reflects on types and dynamically builds Expressions to compile fast ToString methods.
It works well but I've just discovered it errors on DateTimes. It chokes when trying to build an call to StringBuilder.Append(Object) passing a DateTime. Do I need to create an expression to box value types? How is this best done?
I've created the following test case to demonstrate the failure.
// passes
[Test]
public void AppendDateTime()
{
StringBuilder sb = new StringBuilder();
sb.Append(new DateTime());
}
// throws
[Test]
public void ExpressionAppendDateTime()
{
ParameterExpression sbArgExpression = Expression.Parameter(typeof(StringBuilder), "sb");
ParameterExpression dateTimeArgExpression = Expression.Parameter(typeof(DateTime), "dateTime");
var appendMethod = typeof(StringBuilder).GetMethod("Append", new[] {typeof(DateTime)});
var call = Expression.Call(sbArgExpression, appendMethod, dateTimeArgExpression);
// throws on this line
var lambda = Expression.Lambda<Action<StringBuilder, DateTime>>(call, sbArgExpression, dateTimeArgExpression).Compile();
var datetime = new DateTime();
var sb = new StringBuilder();
lambda.Invoke(sb, datetime);
}
The exception is..
System.ArgumentException was unhandled by user code
Message=Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'System.Text.StringBuilder Append(System.Object)'
Source=System.Core
StackTrace:
at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)
at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments)
at Tests.TestToStringBuilder.ExpressionAppendDateTime() in
InnerException:

Solved it, had to use Expression.TypeAs to type non-primitive value types as Object
[Test]
public void ExpressionAppendDateTime()
{
ParameterExpression sbArgExpression = Expression.Parameter(typeof(StringBuilder), "sb");
ParameterExpression dateTimeArgExpression = Expression.Parameter(typeof(DateTime), "dateTime");
var appendMethod = typeof(StringBuilder).GetMethod("Append", new[] {typeof(DateTime)});
Type t = typeof(DateTime);
Expression arg;
if (t.IsValueType && !t.IsPrimitive)
{
arg = Expression.TypeAs(dateTimeArgExpression, typeof(object));
}
else
{
arg = dateTimeArgExpression;
}
var call = Expression.Call(sbArgExpression, appendMethod, arg);
var lambda = Expression.Lambda<Action<StringBuilder, DateTime>>(call, sbArgExpression, dateTimeArgExpression).Compile();
var datetime = new DateTime();
var sb = new StringBuilder();
lambda.Invoke(sb, datetime);
}

Related

Call method Expression Tree in C#

I want create expression with reflection in c#.
I am target script is:
using (var service = new MyProxy<IProductsService>())
{
var response = service.DoAction<Response<List<Product>>>(srv => srv.GetProducts());
}
How to (srv => srv.GetProducts() script is generate with expression tree?
Edit:
I am sorry for the wrong expression.
wherein IProductsService, Response<List<Product>> and GetProducts are actually unknown types. I take generic on runtime.
I wrote the following methods(CreateExpression) to try. But Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter); line gives the following error:
No method 'GetProducts' exists on type
'ServiceFoundation.Response1[System.Collections.Generic.List1[ServiceFoundation.Products.Product]]'.
Next in line for I have not tested it yet.
Method:
private void CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
{
ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter);
Type expressionType = typeof(Expression<>);
Type lambdaType = typeof(LambdaExpression);
Type funcType = typeof(Func<,>);
Type delegateType = funcType.MakeGenericType(interfaceType, methodReturnType);
Type expression = expressionType.MakeGenericType(delegateType);
MethodInfo mI = typeof(Expression).GetMethod("Lambda");
MethodInfo lambda = mI.MakeGenericMethod(delegateType);
var ex = lambda.Invoke(this, new object[] { delegateType, property, parameter });
}
Hopefully I can express.
If you have an
Expression<Action<Response>>
you can call
.Compile()
on it and it returns an
Action<Response>
which you can then invoke normally.
eg
Expression<Action<Response>> exp = resp => Console.WriteLine(resp);
Action<Response> func = exp.Compile();
func(myResponse);
However, if that's all you need to do, you might find it simpler not to use expressions at all;
Action<Response> func = resp => Console.WriteLine(resp);
func(myResponse);
I succeeded after a long struggle. I examined different questions here.
and in the end I solved this way:
private Expression CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
{
ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
Expression callExpression = Expression.Call(parameter, methodName.Name,null, null);
Type expressionType = typeof(Expression<>);
Type lambdaType = typeof(LambdaExpression);
Type funcType = typeof(Func<,>).MakeGenericType(interfaceType, methodReturnType);
Type expressionGenericType = expressionType.MakeGenericType(funcType);
string methodSignature = "System.Linq.Expressions.LambdaExpression Lambda(System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])";
var lambdaMethod = typeof(Expression).GetMethods()
.Single(mi => mi.ToString() == methodSignature);
Expression lambdaExpression = (Expression)lambdaMethod.Invoke(this, new object[] { callExpression, new ParameterExpression[] { parameter } });
return lambdaExpression;
}

Creating Expression with out parameter and nullable types

I am trying to build an expression that will call a method with an out parameter. So far I've had success, except when it comes to nullable versions of the parameters.
For this purpose lets suppose the int.TryParse(string, out int) method. I've successfully been able to build an expression (no nullables) by defining a delegate type for this purpose:
internal delegate bool TestDelegate(string input, out int value);
public static MethodInfo GetMethod()
{
return typeof(int).GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string), typeof(int).MakeByRefType() }, null);
}
public static void NormalTestExpression()
{
var method = GetMethod();
var pValue = Expression.Parameter(typeof(string), "str");
var pOutput = Expression.Parameter(typeof(int).MakeByRefType(), "value");
var call = Expression.Call(method, pValue, pOutput);
var lamd = Expression.Lambda<TestDelegate>(call, pValue, pOutput);
var func = lamd.Compile();
int output;
var result = func("3", out output);
// THIS WORKS!!!
}
I am trying to make this work with nullable types. Take for example:
internal delegate bool TestNullableDelegate(string input, out int? value);
The below will fail with an Argument Exception (GetMethod() is retrieving the correct method based off of the primitive type--same method from above)
public static void WithNullableTypeFails()
{
var method = GetMethod();
var pValue = Expression.Parameter(typeof(string), "str");
var pOutput = Expression.Parameter(typeof(int?).MakeByRefType(), "value");
value
var call = Expression.Call(method, pValue, pOutput); //Argument Exception int.TryParse() doesn't accept int? argument
var lamd = Expression.Lambda<TestNullableDelegate>(call, pValue, pOutput);
var func = lamd.Compile();
}
Expression of type 'System.Nullable`1[System.Int32]' cannot be used for parameter of type 'System.Int32' of method 'Boolean TryParse(System.String, Int32 ByRef)'
Now I am aware that this is because I am still invoking the MethodInfo which is taking the primitive int type and the delegates aren't matching. So I tried the below:
public static void WithNullableTypeAttempt()
{
var method = GetMethod();
var pValue = Expression.Parameter(typeof(string), "str");
var pOutput = Expression.Parameter(typeof(int?).MakeByRefType(), "value");
var vars = Expression.Variable(typeof(int), "tmp");
var resultvar = Expression.Variable(typeof(bool), "result");
var call = Expression.Call(method, pValue, vars);
var block = Expression.Block(
Expression.Assign(vars, Expression.Constant(default(int))),
Expression.Assign(resultvar, call),
Expression.Assign(pOutput, Expression.Convert(vars, typeof(int?))),
resultvar
);
var lamd = Expression.Lambda<TestNullableDelegate>(block, pValue, pOutput);
var func = lamd.Compile(); // Invalid Operation Exception
}
I get an invalid operation exception when I attempt to compile it:
variable 'tmp' of type 'System.Int32' referenced from scope '', but it is not defined
When I open the expression in one of the expression tree visualizers I have, I see the following:
(valueToParse, value) =>
{
var tmp = 0;
var result = int.TryParse(valueToParse, out tmp);
var value = (int?)tmp;
return result;
}
So I think I am on the right track.
How can I call this method where the types vary only by the Nullable type, keeping the delegate with the Nullable type?
You are currently using an overload of the method that does not support variables.
Quoting from the reference above:
Creates a BlockExpression that contains four expressions and has no variables.
You need to tell the block expression about the variables by using an overload that supports variables like this:
var block = Expression.Block(
new ParameterExpression[] { vars, resultvar }, //variables
Expression.Assign(vars, Expression.Constant(default(int))),
Expression.Assign(resultvar, call),
Expression.Assign(pOutput, Expression.Convert(vars, typeof(int?))),
resultvar);

Expression Lambda with types known at runtime

I am trying to make some Expressions where I will be using a lambda to create two methods: a selector and condition. Simplified the usage is condition(selector(data)), but the intemediate type is only known at runtime. I have the following code which works as long as the intemediate type is object, but at runtime I know the real type and would like to use that.
public static ICondition<TData> GetRelayConditionByReflection(string name, string message, string assemblyName,
string fullyQualifiedName, string conditionMethodName, string selectorMethodName) {
var asm = Assembly.LoadFrom(assemblyName);
var type = asm.GetType(fullyQualifiedName);
var selectorMi = type.GetMethod(selectorMethodName, BindingFlags.Static | BindingFlags.Public);
var conditionMi = type.GetMethod(conditionMethodName, BindingFlags.Static | BindingFlags.Public);
var tCondType = selectorMi.ReturnType;
var returnType = typeof(RelayCondition<,>);
var typeArgs = new[] { typeof(TData), tCondType };
var paramTData = Expression.Parameter(typeof(TData), "data");
var selector = Expression.Lambda<Func<TData, object>>(
Expression.Call(selectorMi, paramTData), paramTData).Compile();
var paramTCondition = Expression.Parameter(tCondType, "condition");
var condition = Expression.Lambda<Func<object, bool>>(
Expression.Call(conditionMi, paramTCondition), paramTCondition).Compile();
return (ICondition<TData>)Activator.CreateInstance(returnType.MakeGenericType(typeArgs), name, condition, message, selector);
}
Specifically, there is Expression.Lambda<Func<TData, object>> and Expression.Lambda<Func<object, bool>> where object currently only allows the intemediate type to be object.
Is it possible to create this with a type known only at runtime? I am open for other approaches for the entire problem, as long as performance isn't attrocius.
If you don't need implicit downcasting (for example declaring a Func<object> when in truth you method returns a Func<Foo>), you can use the non-generic Expression.Lambda() method. It returns a LambdaExpression, but in truth it returns a Expression<> of Func<> or of Action downcasted to LambdaExpression (Expression<> is a subclass of LambdaExpression), so:
var condition = Expression.Lambda(
Expression.Call(conditionMi, paramTCondition), paramTCondition).Compile();
now condition is a Delegate, but in truth it is a Func<> (or an Action if it had a return type void).

Supporting "out / ref" parameters in expressions with conversion to "object"

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

Is it possible to have an out ParameterExpression?

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#!

Categories

Resources