Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert string into func

Tags:

c#

linq

I have simple lambda as a string:

var str = "f => f.Substring(0, f.IndexOf(' '))";

which eventualy takes first word from the passed string.

What is the easiest way to compile/convert this peace of string into Func and make the following code to work.

Func<string, string> func = ...
var firstWord = func("Hello World");

Will Expression help me? Will appreciate working sample,

Thanks

like image 362
user1153896 Avatar asked May 18 '12 15:05

user1153896


2 Answers

Problem is that there is no strong typing support here, so you can't even utilize expression trees. However, DynamicLinq already does this, so you can easily get this working with a few modifications. Just create a few static methods of your own in the DynamicExpression class to add this functionality:

public static Expression<Func<T, S>> ParseLambda<T, S>(string expression)
{
    string paramString = expression.Substring(0, expression.IndexOf("=>")).Trim();
    string lambdaString = expression.Substring(expression.IndexOf("=>") + 2).Trim();
    ParameterExpression param = Expression.Parameter(typeof(T), paramString);
    return (Expression<Func<T,S>>)ParseLambda(new[] { param }, typeof(S), lambdaString, null);
}

public static LambdaExpression ParseLambda(string expression, Type returnType, params Type[] paramTypes)
{
    string paramString = expression.Substring(0, expression.IndexOf("=>")).Trim("() ".ToCharArray());
    string lambdaString = expression.Substring(expression.IndexOf("=>") + 2).Trim();
    var paramList = paramString.Split(',');
    if (paramList.Length != paramTypes.Length)
        throw new ArgumentException("Specified number of lambda parameters do not match the number of parameter types!", "expression");

    List<ParameterExpression> parameters = new List<ParameterExpression>();
    for (int i = 0; i < paramList.Length; i++)
        parameters.Add(Expression.Parameter(paramTypes[i], paramList[i].Trim()));

    return ParseLambda(parameters.ToArray(), returnType, lambdaString, null);
}

Usage for both:

Func<string, string> func = DynamicExpression.ParseLambda<string, string>("f => f.Substring(0, f.IndexOf(\" \"))").Compile();

Func<string, int, string> otherFunc = ((Expression<Func<string, int, string>>)DynamicExpression.ParseLambda("(str, ind) => (ind * 100).ToString() + str")).Compile();

Edit: This has not been thoroughly tested, so you might want to make sure with some unit tests that these produce the correct results. I'm not doing anything overly complicated, just explicitly declaring the parameters to use in the public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) method.
Also fixed bug pointed out by @Mauro Destro by trimming the parameter name from paramList when creating the ParameterExpression.

like image 139
SPFiredrake Avatar answered Oct 21 '22 06:10

SPFiredrake


You can use Roslyn scripting to do this. For example:

var str = "f => f.Substring(0, f.IndexOf(' '))";
var func = await CSharpScript.EvaluateAsync<Func<string, string>>(str);
var firstWord = func("Hello World");

This requires the Microsoft.CodeAnalysis.CSharp.Scripting package.

like image 6
svick Avatar answered Oct 21 '22 07:10

svick