none
String als Code oder "Interpreter-Emulation"... Zeichenkette als Funktion RRS feed

  • Frage

  • Hallo,

    leider gibt es ja kein "eval" wie in der "guten" (?), alten (!) und kruden Programmiersprache XBase++ in C#. Dennoch würde mich interessieren, wie man dieses Verhalten möglichst simpel (keine Sicherheitsaspekte und Ähnliches) in C# nachbilden kann.

    Konkreter: natürlich ist es möglich, einer Variablen einen Lambda-Ausdruck zuzuweisen und diese Variable auf Daten anzuwenden. Den Lambda-Ausdruck muss ich aber im Code hinschreiben. Wenn ich aber zur Design-Zeit noch nicht genau weiß, wie der Lambda-Ausdruck (oder auch etwas ganz anderes, halt ein sinnvolles Code-Fragment) zur Laufzeit aussieht, bringt mir das nicht viel. Triviales Beispiel (trivial, weil dämlich, weil viel einfacher lösbar, aber anschaulich):

    public float Brutto(float netto, float mwstInProzent) { return netto * (1 + mwstInProzent / 100); }
    ...
    var brutto = Brutto(netto, Consts.GermanVATInPercent);

    wäre naheliegend. Es ginge aber auch

    public float Brutto(float netto) { return netto * 1.19; }
    ...
    var brutto = Brutto(netto);

    Was ich nun suche, würde in Pseudocode so aussehen:

    var Brutto = (Code)"return netto * (1 + " + Consts.GermanVATInPercent.ToString() + " / 100);";
    ...
    var Brutto = Brutto(netto);

    Der Typecast kann natürlich auch sonstwie aussehen, eine Methode, irgendwas, aber eben so einfach wie möglich.

    MfG

    Carsten Posingies

    Freitag, 24. Oktober 2014 13:12

Antworten

  • Hallo,
    .NET bietet auch so eine Art eval an - dabei wird der jeweilige .NET Code kompiliert. Das entspricht dem, was in dem verlinkten Codeproject Beitrag beschrieben ist. Ich kann den Code bei mir Problemlos ausführen:

    public static void HelloWorld()
    {
        string code = @"
            using System;
    
            namespace First
            {
                public class Program
                {
                    public static void Main()
                    {
                    " +
                        "Console.WriteLine(\"Hello, world!\");"
                        + @"
                    }
                }
            }
        ";
    
        CSharpCodeProvider provider = new CSharpCodeProvider();
        CompilerParameters parameters = new CompilerParameters();
    
        // Reference to System.Drawing library
        parameters.ReferencedAssemblies.Add("System.Drawing.dll");
        // True - memory generation, false - external file generation
        parameters.GenerateInMemory = true;
        // True - exe file generation, false - dll file generation
        parameters.GenerateExecutable = true;
    
        CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
    
        if (results.Errors.HasErrors)
        {
            StringBuilder sb = new StringBuilder();
    
            foreach (CompilerError error in results.Errors)
            {
                sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
            }
    
            throw new InvalidOperationException(sb.ToString());
        }
    
        Assembly assembly = results.CompiledAssembly;
        Type program = assembly.GetType("First.Program");
        MethodInfo main = program.GetMethod("Main");
    
        main.Invoke(null, null);
    }

    Darum ist es wichtig, das du uns sagst, wo der Fehler auftritt und welche zusätzlichen Informationen du bekommst. Zeige bitte auch deinen aktuellen Code dazu.

    Eine andere Möglichkeit besteht natürlich darin einen eigenen Parser zu schreiben. Du scheinst es aber speziell auf C# spezifische Syntax abzusehen, weswegen CodeDom der einfacherere sein dürfte.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 24. Oktober 2014 14:45
    Moderator

Alle Antworten

  • Nachtrag: Eine Lösung habe ich gefunden, die leider nicht funktioniert:

    http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime

    Da steigt es mit einer System.IO.FileNotFoundException aus.

    Freitag, 24. Oktober 2014 13:53
  • Hallo,
    .NET bietet auch so eine Art eval an - dabei wird der jeweilige .NET Code kompiliert. Das entspricht dem, was in dem verlinkten Codeproject Beitrag beschrieben ist. Ich kann den Code bei mir Problemlos ausführen:

    public static void HelloWorld()
    {
        string code = @"
            using System;
    
            namespace First
            {
                public class Program
                {
                    public static void Main()
                    {
                    " +
                        "Console.WriteLine(\"Hello, world!\");"
                        + @"
                    }
                }
            }
        ";
    
        CSharpCodeProvider provider = new CSharpCodeProvider();
        CompilerParameters parameters = new CompilerParameters();
    
        // Reference to System.Drawing library
        parameters.ReferencedAssemblies.Add("System.Drawing.dll");
        // True - memory generation, false - external file generation
        parameters.GenerateInMemory = true;
        // True - exe file generation, false - dll file generation
        parameters.GenerateExecutable = true;
    
        CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
    
        if (results.Errors.HasErrors)
        {
            StringBuilder sb = new StringBuilder();
    
            foreach (CompilerError error in results.Errors)
            {
                sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
            }
    
            throw new InvalidOperationException(sb.ToString());
        }
    
        Assembly assembly = results.CompiledAssembly;
        Type program = assembly.GetType("First.Program");
        MethodInfo main = program.GetMethod("Main");
    
        main.Invoke(null, null);
    }

    Darum ist es wichtig, das du uns sagst, wo der Fehler auftritt und welche zusätzlichen Informationen du bekommst. Zeige bitte auch deinen aktuellen Code dazu.

    Eine andere Möglichkeit besteht natürlich darin einen eigenen Parser zu schreiben. Du scheinst es aber speziell auf C# spezifische Syntax abzusehen, weswegen CodeDom der einfacherere sein dürfte.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 24. Oktober 2014 14:45
    Moderator