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?
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.
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 .
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.
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.
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
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);
}
}
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.
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
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:
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With