Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create anonymous method from a string in c#

is it possible to create an anonymous method in c# from a string?

e.g. if I have a string "x + y * z" is it possible to turn this into some sort of method/lambda object that I can call with arbitrary x,y,z parameters?

like image 841
toasteroven Avatar asked Oct 15 '09 20:10

toasteroven


People also ask

What is anonymous function in C?

Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function.

How do I make an anonymous object?

You create anonymous types by using the new operator together with an object initializer. For more information about object initializers, see Object and Collection Initializers. The following example shows an anonymous type that is initialized with two properties named Amount and Message .

What is anonymous method in C sharp?

Anonymous methods provide a technique to pass a code block as a delegate parameter. Anonymous methods are the methods without a name, just the body. You need not specify the return type in an anonymous method; it is inferred from the return statement inside the method body.

Which keyword is used to declare an anonymous method?

In Python, an anonymous function is a function that is defined without a name. While normal functions are defined using the def keyword in Python, anonymous functions are defined using the lambda keyword. Hence, anonymous functions are also called lambda functions.


5 Answers

It's possible, yes. You have to parse the string and, for example, compile a delegate using expression trees.

Here's an example of creating (x, y, z) => x + y * z using expression trees:

ParameterExpression parameterX = Expression.Parameter(typeof(int), "x");
ParameterExpression parameterY = Expression.Parameter(typeof(int), "y");
ParameterExpression parameterZ = Expression.Parameter(typeof(int), "z");
Expression multiplyYZ = Expression.Multiply(parameterY, parameterZ);
Expression addXMultiplyYZ = Expression.Add(parameterX, multiplyYZ);
Func<int,int,int,int> f = Expression.Lambda<Func<int, int, int, int>>
(
    addXMultiplyYZ,
    parameterX,
    parameterY,
    parameterZ
).Compile();
Console.WriteLine(f(24, 6, 3)); // prints 42 to the console
like image 163
jason Avatar answered Oct 03 '22 13:10

jason


Just for fun using CodeDom (any valid C# code is allowed in the string as long as it is present in mscorlib (No check for errors at all):

static class Program
{
    static string code = @"
        public static class __CompiledExpr__
        {{
            public static {0} Run({1})
            {{
                return {2};
            }}
        }}
        ";

    static MethodInfo ToMethod(string expr, Type[] argTypes, string[] argNames, Type resultType)
    {
        StringBuilder argString = new StringBuilder();
        for (int i = 0; i < argTypes.Length; i++)
        {
            if (i != 0) argString.Append(", ");
            argString.AppendFormat("{0} {1}", argTypes[i].FullName, argNames[i]);
        }
        string finalCode = string.Format(code, resultType != null ? resultType.FullName : "void",
            argString, expr);

        var parameters = new CompilerParameters();
        parameters.ReferencedAssemblies.Add("mscorlib.dll");
        parameters.ReferencedAssemblies.Add(Path.GetFileName(Assembly.GetExecutingAssembly().Location));
        parameters.GenerateInMemory = true;

        var c = new CSharpCodeProvider();
        CompilerResults results = c.CompileAssemblyFromSource(parameters, finalCode);
        var asm = results.CompiledAssembly;
        var compiledType = asm.GetType("__CompiledExpr__");
        return compiledType.GetMethod("Run");
    }

    static Action ToAction(this string expr)
    {
        var method = ToMethod(expr, new Type[0], new string[0], null);
        return () => method.Invoke(null, new object[0]);
    }

    static Func<TResult> ToFunc<TResult>(this string expr)
    {
        var method = ToMethod(expr, new Type[0], new string[0], typeof(TResult));
        return () => (TResult)method.Invoke(null, new object[0]);
    }

    static Func<T, TResult> ToFunc<T, TResult>(this string expr, string arg1Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T) }, new string[] { arg1Name }, typeof(TResult));
        return (T arg1) => (TResult)method.Invoke(null, new object[] { arg1 });
    }

    static Func<T1, T2, TResult> ToFunc<T1, T2, TResult>(this string expr, string arg1Name, string arg2Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T1), typeof(T2) },
            new string[] { arg1Name, arg2Name }, typeof(TResult));
        return (T1 arg1, T2 arg2) => (TResult)method.Invoke(null, new object[] { arg1, arg2 });
    }

    static Func<T1, T2, T3, TResult> ToFunc<T1, T2, T3, TResult>(this string expr, string arg1Name, string arg2Name, string arg3Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T1), typeof(T2), typeof(T3) },
            new string[] { arg1Name, arg2Name, arg3Name }, typeof(TResult));
        return (T1 arg1, T2 arg2, T3 arg3) => (TResult)method.Invoke(null, new object[] { arg1, arg2, arg3 });
    }

    static void Main(string[] args)
    {
        var f = "x + y * z".ToFunc<int, int, long, long>("x", "y", "z");
        var x = f(3, 6, 8);

    }
}
like image 26
Julien Roncaglia Avatar answered Oct 04 '22 13:10

Julien Roncaglia


C# doesn't have any functionality like this (other languages - like JavaScript - have eval functions to handle stuff like this). You will need to parse the string and create a method yourself with either expression trees or by emitting IL.

like image 21
Andrew Hare Avatar answered Oct 03 '22 13:10

Andrew Hare


There are functionality to do this in the .Net framework.

It is not easy. You need to add some code around the statement to make it into a complete assembly including a class and method you can call.

After that you pass the string to

CSharpCodeProvider.CompileAssemblyFromSource(options, yourcode);

Here is an example

like image 32
adrianm Avatar answered Oct 05 '22 13:10

adrianm


It could be possible with a grammar (e.g. ANTLR) and an interpreter which creates expression trees. This is no small task, however, you can be successful if you limit the scope of what you accept as input. Here are some references:

  • C# ANTLR3 Grammar (complex, but you could extract a portion)
  • Expression Trees

Here is what some code may look like to transform an ANTLR ITree into an Expression tree. It isn't complete, but shows you what you're up against.

private Dictionary<string, ParameterExpression> variables
    = new Dictionary<string, ParameterExpression>();

public Expression Visit(ITree tree)
{
    switch(tree.Type)
    {
        case MyParser.NUMBER_LITERAL:
            {
                float value;
                var literal = tree.GetChild(0).Text;
                if (!Single.TryParse(literal, out value))
                    throw new MyParserException("Invalid number literal");
                return Expression.Constant(value);
            }

        case MyParser.IDENTIFIER:
            {
                var ident = tree.GetChild(0).Text;
                if (!this.variables.ContainsKey(ident))
                {
                    this.variables.Add(ident,
                       Expression.Parameter(typeof(float), ident));
                }

                return this.variables[ident];
            }

        case MyParser.ADD_EXPR:
            return Expression.Add(Visit(tree.GetChild(0)), Visit(tree.GetChild(1)));

        // ... more here
    }
}
like image 41
user7116 Avatar answered Oct 03 '22 13:10

user7116