Improve string.Format CallExpression
-
29-06-2021 - |
题
I am trying to call string.Format
using Tree
It took me a bit of work since my supply params Expression[] _ParameterExpressions
does not match signature of string.Format
which accept object[]
it seems it won't apply implicit conversion.
My current solution is to convert my supplied parameter into object[]
using
NewArrayExpression _NewArray = Expression.NewArrayInit(typeof(object), _ParameterExpressions.Select(ep => Expression.Convert(ep, typeof(object))));
and setup my proxy function to pass parameters to string.Format
(I need this or else it would say it could not find matched signature)
static string ReplaceParameters(string format, params object[] obj)
{
return string.Format(format, obj);
}
static IEnumerable<Expression> ReplaceStringExpression(Expression exp)
{
yield return exp;
yield return _NewArray;
}
And finally my call
ConstantExpression ce = Expression.Constant(orginalString, typeof(string));
MethodCallExpression me = Expression.Call(typeof(RuleParser), "ReplaceParameters", null,
ReplaceStringExpression(ce).ToArray());
The expression works but I don't really like the idea of creating new array which includes extra boxing process. I think it was overdone on such simple function call.
How can I improve this string.Format
call?
==========
Edit
Have some progress on my study. I can now ditch ReplaceParameters
but still don't like to create array of object _NewArray
MethodCallExpression me = Expression.Call(
typeof(string).GetMethod("Format", new Type[2] { typeof(string), typeof(object[]) }),
ReplaceStringExpression(ce).ToArray());
解决方案
When ExpressionTree is created by the compiler - it will contain all implicit required conversions to make selected method overload work
Expression<Func<string>> compilerFactoredExpressionTree = () => string.Format("{0} and {1} and {2} and {3}", 1, "text", true, 2); // test // "1 and text and True and 2" string s = compilerFactoredExpressionTree.Compile()(); // ArrayInit(Convert(1), Convert("text", Convert(2))) Expression methodArgs = ((MethodCallExpression)compilerFactoredExpressionTree.Body).Arguments[1];
If you construct ExpressionTree manually - you'll need to act as compiler yourself - insert conversions by hand or initially declare values with required type
var parameters = new Expression[] { Expression.Constant(1, typeof(object)), Expression.Constant("text", typeof(object)), Expression.Constant(true, typeof(object)), Expression.Constant(2, typeof(object)), }; var mi = new Func<string, object[], string>(string.Format).Method; var callStringFormat = Expression.Call(mi, Expression.Constant("{0} and {1} and {2} and {3}"), Expression.NewArrayInit(typeof(object), parameters)); // "1 and text and True and 2" var lambda = Expression.Lambda<Func<string>>(callStringFormat); var r = lambda.Compile()();