2009-10-15 19 views
5

bir dizeden C# olarak anonim bir yöntem oluşturmak mümkün mü?Bir dize bir dizeden anonim yöntem oluşturma C#

örn. Bir string "x + y * z" varsa, bunu rasgele x, y, z parametreleriyle çağırabileceğim bir yöntem/lambda nesnesine dönüştürmek mümkün müdür?

+0

Ben microsoft'ta biraz adam .net 4 böyle şeyler başarmak için bir hizmet olarak kullanılabilir olan derleyici hakkında bir yayın yaptılar eminim bu kadar bilmiyorum senin. davası olsa da. –

+1

Anders, PDC'de "bir hizmet olarak derleyici" den bahsederken çok ilerideki çalışmalardan bahsediyordu. C# 4 kesinlikle böyle bir işe sahip olmayacak. –

+1

Çoğalt: http://stackoverflow.com/questions/1437964/best-and-shortest-way-to-evaluate-mathematical-expressions/ Bu soru, aynı zamanda, bundan daha spesifik olarak yanıtlandı. – Joren

cevap

13

Bu mümkün, evet. Dizeyi ayrıştırmanız ve örneğin ifade ağaçları kullanarak bir delege oluşturmanız gerekir.

Burada (x, y, z) => x + y * z kullanılarak ifade ağaçları yaratan bir örnek verilmiştir:

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 Bu iyi bir örnektir, ancak söz konusu dizeye uyarlanmıştır. Bu örnek, OP'nin rastgele bir dizeyi ayrıştırmasına, dizgedeki tanımlayıcıların türlerini çıkarmasına ve bulunana dayalı bir yöntem oluşturmasına yardımcı olmaz. Yine de, iyi örnek için size + 1. –

+0

+1, ve artık 9999'a! –

+1

Ayrıştırma işleminin, OP'ye ifade ağaçlarından çok daha tanıdık olacağını (en azından ayrıştırma konusundaki literatürün daha büyük olduğunu) varsaydım. İfade ağacının amacı, yalnızca teknolojiyi kendisine göstermek ve güçlerini göstermek, genel problemi çözmek değil. – jason

5

C#, bu gibi bir işleve sahip değildir (diğer diller - JavaScript gibi) - eval gibi işlevlerin üstesinden gelmek için işlevler). Dizeyi ayrıştırmanız ve ifade ağaçları ile veya IL yayarak bir yöntem oluşturmanız gerekir.

1

bir gramer (örneğin ANTLR) ve ifade ağaçlar yaratan bir yorumlayıcı mümkün olabilir. Bu küçük bir görev değildir, ancak giriş olarak kabul ettiğiniz şeyin kapsamını sınırlarsanız başarılı olabilirsiniz. İşte

  • Expression Trees
  • bazı kod İfade ağacına bir ANTLR ITree dönüştürmek gibi görünebilir ne

    • C# ANTLR3 Grammar (karmaşık, ancak bir kısmını ayıklamak olabilir): İşte bazı referanslar bulunmaktadır. Tamamlanmadı, ama neyle karşı karşıya olduğunuzu gösterir.

      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 çerçevesinde bunu yapmak için işlevsellik vardır.

    Kolay değildir. İfadenin etrafına, arayabileceğiniz bir sınıf ve yöntem de dahil olmak üzere eksiksiz bir mecraya dönüştürmek için bir kod eklemeniz gerekir.

    Bundan sonra CodeDOM kullanarak Sadece eğlence için

    CSharpCodeProvider.CompileAssemblyFromSource(options, yourcode); 
    

    Here is an example

    +0

    Hangi parametrelerin gerekli olduğunu bulmanın yanı sıra, CompileAssemblyFromSource ile arkadaşlık kurmak zor. – user7116

    7

    dize geçirir (herhangi bir geçerli C# kod yok onay için (mscorlib mevcut olduğu sürece dize izin verilir hiç hataları):

    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); 
    
        } 
    } 
    
    İlgili konular