Exception when building Expression to call StringBuilder.Append(Object) with DateTime
I've built a ToStringBuilder (found here) which reflects on types and dynamically builds Expression
s 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);
}
精彩评论