Laden...

[gelöst] Lambda Funktionen in einem Script bzw. zur Laufzeit kompilierten Code.

Erstellt von dr4g0n76 vor 14 Jahren Letzter Beitrag vor 13 Jahren 7.071 Views
dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren
[gelöst] Lambda Funktionen in einem Script bzw. zur Laufzeit kompilierten Code.

Hallo Leute!

Ich hatte ja hier mal die Projekte Funktionsplotter und Formeleditor veröffentlicht.
Diese nutzen für die Funktionalität zur Laufzeit übersetzten Code.

Jetzt kam ich gestern auf die Idee das ganze von außen zu erweitern, in dem ich an einer Stelle einen Lambda-Ausdruck verwende, dieser ist Basis für ein Delegate
das an eine Klasse übergeben wird, die diesen Ausdruck auswertet bzw. ausführt.

Wenn ich keinen zur Laufzeit übersetzten Code benutze, funktioniert das ganze.
Wenn ich aber den oben beschriebenen Mechanismus benutze, behauptet der Compiler es würde eine ")" fehlen. Füge ich eine hinzu, verlangt er noch eine usw.

Jetzt meine Frage:

Ist es möglich, dass man etwas spezielles beachten muss, wenn man einen Lambda-Ausdruck auf die oben beschriebene Methode zur Laufzeit kompilieren möchte?

P.S.:

Falls das ganze nicht ganz klar ist, ich kann auch gerne den generierten Code und das Projekt dazu posten.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

Gelöschter Account
vor 14 Jahren

das musst du durch die Expression<T> klasse schleusen. diese hat dann eine Compile methode, welche dir dann irgendwie etwas IL code leifert oder so ähnlich. ich baue mir privat auch sowas für wpf und bindings, damit ich ein wenig mehr tricksen kann aber ich konnte ncoh nicht ausreichend zeit investieren um durchzusteigen.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

@JAck30lena:

Ok, das erklärt das Problem. 😉 Denn der generierte Code ist eine vollständige in C# generierte Klasse und lässt sich nämlich im Compiler ohne Probleme MIT Lambda-Ausdruck komplett übersetzen.

Aber auch wenn ich den Lambda-Ausdruck herauswerfe und das Projekt komplett zur Laufzeit kompilieren lasse, funktioniert es.

Dann werde ich wohl auch mal Zeit investieren müssen. Können wir uns ja gegenseitig helfen. 😉

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

5.941 Beiträge seit 2005
vor 14 Jahren

Hallo dr4g0n76

Stichwort: Expression Tree, Artikel dazu: [Artikel] Delegaten, anonyme Methoden, Lambda-Ausdrücke & Co.

Ein ExpressionTree bildet schlussendlich nur eine Funktion ab, nicht mehr und nicht weniger.
Daher denke ich, das es für dich nicht geeignet ist, so wie du dein Szenario beschreibst.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

Danke für den Tipp @ Peter Bucher,

hier ist ebenfalls etwas womit man einen Versuch starten könnte:

Using Expressions for Fun and Profit

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

Gelöschter Account
vor 14 Jahren

hier habe ich noch etwas interessantes:
http://www.ohloh.net/p/linq2codedom

2.891 Beiträge seit 2004
vor 14 Jahren

Hallo dr4g0n76,

Falls das ganze nicht ganz klar ist...

Hm, so ganz hab ich's noch nicht... Willst du aus einer Zeichenkette (die einen Lambda-Ausdruck darstellt) zur Laufzeit auswerten und dann "ausführen"? Dann hilft dir vielleicht
Expression<Func<T, T>> aus string erzeugen?

Gruß,
dN!3L

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

@dN!3L:

Untenstehende Klasse wird zur Laufzeit erzeugt als String und dann kompiliert.


        using LambdaImageProcessing;
        using System.Drawing;

        public class LambdaScript
        {
            LambdaCommand command = new LambdaCommand();

            public LambdaScript()
            {
            }

            public void Test(Bitmap _bitmap)
            {
                command.Apply(_bitmap,
                    (Color c, int x, int y) => Color.FromArgb(c.R, c.G, c.B)  // <== hier
                );
            }
        }

An der fett markierten Zeile stört sich der Compiler zur Laufzeit, wenn ich die entsprechenden Befehle benutze und dann CompileFromSourceCode aufrufe.

Die Fehlermeldung die ich erhalte:

) erwartet.

Genau diesen String, nicht mehr und nicht weniger erhalte ich, eine ")" hinzuzufügen, kann ich so oft ich will der Fehler bleibt immer gleich.

Wird die Klasse aber genau so in das Projekt DIREKT eingebunden und NICHT zur Laufzeit generiert, kann Visual Studio das ganze ohne Probleme übersetzen und ausführen.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

S
443 Beiträge seit 2008
vor 14 Jahren

Kann sein dass ich da jetzt daneben liege, aber:
ich kompilere mit CodeDom zur Laufzeit code (string => dll)

und da musste ich dem CodeDom Compiler sagen das ich 3.5 haben will

vielleicht fehlt das da einfach auch.
so in der Nähe von CompilerVersion("3.5");

Der Link passt zwar nicht ganz zum Thema aber die angesprochene Zeile kommt vor.
CompilerVersion

Ich weis dass Du was anderes hast, aber vielleicht konnte ich eine Richtung vorgeben.

// Edit:
bei mir hatte er ohne der richtigen Compiler version nämlich genau probleme mit dem Lambda, ohne lambda gings auch ohne Compilerversion
Wenn es bei dir ohne Lambda geht, würde ich in die gleiche Richtung tippen.

mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

@spike24:

Danke für den Hinweis:

anscheinend geht auch so was:


using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
class Program
{
    static void Main(string[] args)
    {
        var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
        var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
        parameters.GenerateExecutable = true;
        CompilerResults results = csc.CompileAssemblyFromSource(parameters,
        @"using System.Linq;
            class Program {
              public static void Main(string[] args) {
                var q = from i in Enumerable.Range(1,100)
                          where i % 2 == 0
                          select i;
              }
            }");
        results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
    }
}

Wichtig ist anscheinend, dass es eben NICHT "3.5" sondern explizit "v3.5" heißen muss, d.h. das v ist wichtig.

Danke für den Hinweis. 😉

d.h. in meinem Code habe ich jetzt:


		public CReflectiveEvaluator()
            : this(new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }))
		{
		}

geschrieben. Was tatsächlich eine Änderung bewirkt, nur kann jetzt der Typ zur Laufzeit nicht erstellt werden. Die Kompilierung schlägt aber nicht mehr fehl.

EDIT:

Ich hab auf den falschen Namespace zugegriffen. Den gesamten Typnamen habe ich vollqualifiert einfach aus


compilerResults.CompiledAssembly.GetTypes()

mit dem Debugger ermittelt. Jetzt lässt sich alles übersetzen. UND.... (Test).... Geil! Läuft. Problem gelöst.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.891 Beiträge seit 2004
vor 14 Jahren
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));  

Wenn man einen Hammer hat, sieht alles aus wie ein Nagel... 😁

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

@spike24: Danke, deine Lösung hats gebracht. Super Tipp!

@dN!3L: LOL. 😉 Hatte das aus einem Beispiel. Quasi zur gleichen Zeit entdeckt wie Spike den Tipp gepostet hatte:

c# 3.0 / 3.5 und codedom

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

Hintergrund: Hier noch was ich damit gebastelt hab und warum:


            string sText = new LambdaScript().Source.Replace("{0}", "Color.FromArgb(255-c.R, 255-c.G, 255-c.B)");

            object obj = ev.Eval(sText, "LambdaScript", "Test", new object[] { (Bitmap)this.BackgroundImage });

Ein Benutzer soll einen Ausdruck zur Laufzeit eingeben können.

Daraus werden dann bestimmte Lambdafunktionen generiert, die dann zur Laufzeit pixelweise auf z.B. ein Bild angewendet werden können.

Auf die Idee kam ich als ich

Fast and Simple Bitmap Filter

und kurz danach dann

Steve's Techtalk C#: Image Processing and Lambda Expressions

gelesen hatte.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.891 Beiträge seit 2004
vor 14 Jahren

Kleine Ergänzung:

und da musste ich dem CodeDom Compiler sagen das ich 3.5 haben will [...]
bei mir hatte er ohne der richtigen Compiler version nämlich genau probleme mit dem Lambda, ohne lambda gings auch ohne Compilerversion

Das liegt daran, dass Lambdaausdrücke/Expressions ein Compilerfeature/C# 3.0-Feature (nicht zu verwechseln mit .NET-Framework-Version) sind.

Allerdings hätte ich erwartet, dass der aktuellste Compiler/die neueste C#-Version benutzt wird. Wieder was über CodeDOM gelernt...

Gruß,
dN!3L

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren
Testprojekt zu Runtimelambdas

Hier noch das Testprojekt dazu.

Und die Erklärung:

WindowsFormsApplication1 ist das Projekt mit der Form.
LambdaImageProcessingTools enthält die Routinen zum probieren

und ScriptCompiler2 ist eine Weiterentwicklung des Runtime-Compilers von FormelEditor bzw. Funktionsplotter, mit dem Unterschied zu früher, dass jetzt beliebige Objekte hin- und hergereicht werden können, wie ihr seht.

In diesem Fall eben ein Bild, das reingesteckt, über den Runtime-Lambda-Ausdruck verarbeitet, und dann wieder als Hintergrundbild der Form angezeigt wird.

Es wird also wirklich in den Lambda-Ausdruck reinkompiliert was in die Textbox eingegeben wird.

Das ganze ist nur ein Probierprojekt. Funktioniert aber super, solange beim Eintippen keine Fehler passieren.

EDIT: Ach ja, ganz vergessen, wichtig! Um ein Bild zu laden, Hintergrund mit Links doppelklicken, sonst gibts ne Exception oder evtl. andere Fehler

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.891 Beiträge seit 2004
vor 14 Jahren

Hallo zusammen,

und da musste ich dem CodeDom Compiler sagen das ich 3.5 haben will

Bekommt man eigentlich irgendwie raus, welche Compilerversion man (maximal) zur Verfügung hat?
Mit Environment.Version kommt man ja an die CLR-Version, aber die stimmt ja nicht mit der Compilerversion überein...

Gruß,
dN!3L

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));  

Wenn man einen Hammer hat, sieht alles aus wie ein Nagel... 😄

Ist das eigentlich positiv oder negativ gemeint?!

EDIT/EDIT: 🙂 wie gehen hier nochmal die richtigen Smilies?! ^^

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

S
443 Beiträge seit 2008
vor 14 Jahren

Ich glaube weder noch, meines Erachtens beschreibt es eher die Situation, dass man etwas neues gelernt hat und mit diesem neuen wissen versucht alle bestehenden Probleme zu lösen.
Regular Expressions

mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 13 Jahren

Falls es jemanden interessiert, inzwischen habe ich auch folgendes gelöst:

LINQ Ausdruck zur Laufzeit übergeben.

Im eigentlichen geht das analog mit der selben Technik.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.