2009-10-15 4 views
5

Можно ли создать анонимный метод в C# из строки?Создать анонимный метод из строки в C#

например. если у меня есть строка "x + y * z", можно ли это превратить в какой-то метод/лямбда-объект, который я могу вызвать с произвольными параметрами x, y, z?

+0

Я уверен, что какой-то парень в microsoft сделал трансляцию о .net 4, имеющую компилятор, доступный как услуга, чтобы выполнить такие вещи. Не знаю, случай. –

+1

Андерс говорил о очень многообещающей работе, когда говорил о «компиляторе как услуге» в PDC. C# 4, безусловно, не будет такой работы. –

+1

Duplicate: http://stackoverflow.com/questions/1437964/best-and-shortest-way-to-evaluate-mathematics-expressions/ Этот вопрос был также дан более конкретно, чем этот. – Joren

ответ

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 к вам за хорошим примером. –

+0

+1, а вот до 9999 уже нет! –

+1

Я предполагал, что синтаксический анализ будет более знакомым (по крайней мере, литература по разбору больше) к OP, чем деревья выражений. Цель дерева выражений - только показать ему технологию и показать свою силу, а не решать общую проблему. – jason

5

У C# нет таких функций, как этот (на других языках, например JavaScript, есть функции eval для обработки таких вещей). Вам нужно будет проанализировать строку и создать метод самостоятельно с помощью деревьев выражений или путем испускания ИЛ.

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 есть функциональность.

Это непросто. Вам нужно добавить код вокруг инструкции, чтобы сделать его полной сборкой, включая класс и метод, которые вы можете вызвать.

После этого вы передаете строку

CSharpCodeProvider.CompileAssemblyFromSource(options, yourcode); 

Here is an example

+0

Наряду с выяснением, какие параметры требуются и т. Д. Трудно сделать дружественным с CompileAssemblyFromSource. – user7116

7

Просто для удовольствия, используя CodeDom (любой действительный 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); 

    } 
} 
Смежные вопросы