2009-10-15 69 views
5

是否有可能在c#中從字符串中創建匿名方法?從c#中的字符串創建匿名方法

例如如果我有一個字符串"x + y * z"是否有可能把它變成某種方法/ lambda對象,我可以調用任意x,y,z參數?

+0

我敢肯定有些傢伙在微軟做了一個關於.NET 4有編譯器可以作爲一個服務來完成這樣的事情廣播不知道,如果它是你的。情況雖然。 – 2009-10-15 20:57:47

+1

當他在PDC討論「編譯器即服務」時,Anders正在談論非常遙遠的工作。 C#4肯定沒有這樣的工作。 – 2009-10-15 22:05:36

+1

Duplicate:http://stackoverflow.com/questions/1437964/best-and-shortest-way-to-evaluate-mathematical-expressions/ 這個問題也比這個更具體回答。 – Joren 2009-10-19 10:40:41

回答

13

這是可能的,是的。您必須解析字符串,例如,使用表達式樹編譯委託。

以下是創建(x, y, z) => x + y * z使用表達式樹的例子:

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 
+0

+1這是一個很好的例子,但它是針對有問題的字符串量身定製的。這個例子不會幫助OP分析一個隨機字符串,推斷字符串中標識符的類型,並根據找到的內容創建一個方法。儘管如此,對你來說,+1是一個很好的例子。 – 2009-10-15 20:46:14

+0

+1,這裏是9999不再! – 2009-10-15 20:56:44

+1

我認爲解析將比表達式樹更熟悉(至少解析文獻更大)OP。表達樹的目的僅僅是向他展示技術並展示他們的力量,而不是解決一般問題。 – jason 2009-10-15 21:01:12

5

C#沒有像這樣的功能(其他語言 - 如JavaScript - 有eval函數來處理這樣的東西)。您將需要解析字符串並使用表達式樹或發佈IL自己創建一個方法。

1

這可能是可能的語法(例如ANTLR)和其產生表達式樹的解釋器。這不是一項小任務,但是,如果您限制接受的輸入範圍,則可以取得成功。下面是一些參考:

下面是一些代碼可能看起來像變換的ANTLR ITree成一個表達式樹。它並不完整,但會向你展示你的目標。

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 
    } 
} 
2

在.Net框架中有這樣的功能。

這並不容易。您需要在語句中添加一些代碼,使其成爲一個完整的程序集,包括可以調用的類和方法。

之後,你使用的CodeDOM將字符串傳遞到

CSharpCodeProvider.CompileAssemblyFromSource(options, yourcode); 

Here is an example

+0

隨着搞清楚需要什麼參數,等等,以便與CompileAssemblyFromSource友好相處。 – user7116 2009-10-15 20:56:34

7

只是爲了好玩(任何有效的C#代碼字符串中允許的,只要它存在於mscorlib程序(沒有支票在所有錯誤):

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

    } 
}