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