Question

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());
Was it helpful?

Solution

  1. 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];
    
  2. 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()();
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top