2009-02-03 3 views
8

Gibt es eine Möglichkeit, Code, der in einer Zeichenfolge enthalten ist, mit .net 2.0 dynamisch auszuführen, ähnlich wie eval() in JavaScript oder sp_executeSQL in tsql?Gibt es eine Möglichkeit, eine Zeichenkette in .net, ähnlich wie eval() in Javascript oder dynamischem sql in sql dynamisch auszuführen?

Ich habe einen String-Wert in einer Variablen, die ich irgendwann in meiner Anwendung manipulieren möchte - also würde der Code im Wesentlichen String-Manipulation sein. Ich weiß nicht, welche verschiedenen Manipulationen benötigt werden, also möchte ich, dass sie konfigurierbar sind.

Es ist mir egal, in welcher Sprache der dynamische Code geschrieben ist, was am einfachsten zu implementieren und einfach genug zu schreiben ist.

Zum Beispiel möchte ich Instanzen von 'ersetzen.' Zeichen mit '-', oder entfernen Sie alle Leerzeichen oder ähnliches. Wenn ich dies in SQL tun würde ich dynamischen SQL verwenden, aber ich will es auszuführen in .net Code, so etwas wie diese:

// Get the value to be manipulated 
string s = ... // wherever s comes from 

// Get the manipulation code, eg this might come from a database 
// setting that can be changed without recompiling the .net code. 
string manipulation = Settings.GetSomeValue("ManipulationSetting"); 

// This is what I want to know how to do: apply some manipulation to the string. 
string result = MagicDynamicEvalClass.Eval(manipulation, s); 

// Now I would do stuff with the result. 

Ich könnte möglicherweise nur Ausdrücke Regex Suchen/Ersetzen. Da ich nur String-Manipulation mache, sollte das ausreichen, vorausgesetzt, ich kann clever genug reguläre Ausdrücke schreiben. zB: ‚‘

// Get the value to be manipulated 
string s = ... // wherever s comes from 

// Get the code to use to manipulate s, eg this might come from a database 
// setting that can be changed without recompiling the .net code. 
string findRegex = Settings.GetSomeValue("RegexPattern"); 
string replaceRegex = Settings.GetSomeValue("RegexReplace"); 

// This is what I want to know how to do: apply some manipulation to the string. 
string result = Regex.Replace(s, findRegex, replaceRegex); 

// Now I can do stuff with the result. 

Aber in einigen Fällen meine Manipulation Anforderung könnte hinausgehen, was mit regex möglich ist, oder ich könnte mehrere Schritte anwenden möchten, zB ersetzen mit '-' und strip spaces. Vielleicht könnte ich eine Liste von Regexes zum Suchen/Ersetzen speichern und darüber iterieren ... aber jeder hat einen besseren Vorschlag?

UPDATE - zB mit dynamischen SQL

Ich will nicht eine Lösung, die mich im Voraus zu wissen, was erfordert Manipulationen möglich sind, und ich freue mich wirklich für etwas einfach. zB in SQL würde ich so etwas tun:

declare @s nvarchar(1000) 
declare @manipulation nvarchar(1000) 
declare @result nvarchar(1000) 

-- ... Get the values from wherever they come from 

-- Execute the manipulation dynamically 
EXEC sp_ExecuteSQL @stmt = @manipulation 
    , @params = N'@s nvarchar(1000), @result nvarchar(1000) OUTPUT' 
    , @s = @s, @result = @result OUTPUT 

Dann könnte ich beliebige SQL in meine @manipulation setzen, so etwas wie dieses SET @result = REPLACE (ERSETZEN (@s, ‚- '' '),' ',' ')

Ja, dies würde erfordern, dass ich vorsichtig bin, welche Werte in @manipulation eingegeben werden dürfen, aber es würde mir die Flexibilität geben, die ich in Zukunft brauche.

Ein ähnlicher Ansatz wäre in Javascript möglich, denke ich, mit eval().

UPDATE - Beispiel mit MSScript Steuerung von .net:

This scheint wie ein möglicher Ansatz, wenn auch vielleicht übertrieben für den einfachen Fall, dass ich mit umgehen will. Es verwendet die Microsoft Script Control-Bibliothek, um die Ausführung von beliebigem VBScript zu ermöglichen.

+0

Bitte tun Sie das nicht. Finden Sie eine andere Technik .... – yfeldblum

+0

Was möchten Sie ausführen? C#? VB.NET? C++/CLI? IronPython? .NET ist keine einzige Sprache, also wie genau sollte es einen String ausführen können? Sie müssen die CodeDom-Funktionalität aufrufen, um auf den Compiler für die gewünschte Sprache zuzugreifen. Sie sollten jedoch einen anderen Ansatz finden. – jalf

+0

@Justice: Ich muss zustimmen, das ist wirklich nur eine schreckliche Idee. – casperOne

Antwort

1

Wie die anderen bereits erwähnt ist es nicht wirklich möglich, C# in einer eval() Funktion zu kompilieren. diese funktionalität ist für eine spätere release der clr geplant, die anders auf der PDC vorgeführt wurde.

als eine andere Lösung, wenn Ihre Anwendung in der Lage ist, auf Mono zu laufen, können Sie einfach seine Eval-Funktion verwenden, die C# code, wie Javascript, dynamisch kompilieren kann. Es tut im Grunde bereits, was .net in ein oder zwei Jahren tun kann.

als eine Alternative, wenn Sie Mono nicht verwenden können Sie den Teil schreiben, der die String-Manipulation in Ironruby tut, die Eval() hat. Der Rest Ihres Codes wird nicht einmal wissen, dass Sie Ruby für diese Klasse/Assambly verwenden.

Der Link, den Sie im Update gepostet haben, sieht für einen so einfachen Anwendungsfall ziemlich kompliziert aus. mit IronRuby alle würden Sie tun müssen, ist die MyDynamicEvalClass so etwas schreiben:

class MyDynamicEvalClass 
    def eval(someString,transformString) 
    eval(someString,transformString) 
    end 
end 

und „ManipulationSetting“ mit einigen Ruby-Code zu ersetzen, die eine neue Zeichenfolge

+0

Ich habe das nicht am Ende benutzt, aber ich denke, die Eleganz, zu einer Sprache zu wechseln, die 'eval()' hat, macht es zum praktischsten für das, was ich erreichen wollte. – Rory

1

Ich denke, es ist möglich mit reflection.emit und codedom, dies zu tun, aber es ist überhaupt nicht trivial und ich rate davon ab.

Als Alternative könnten Sie versuchen, eine Format-Zeichenfolge zu konfigurieren, möglicherweise zusätzlich zur Regex.

+0

was meinst du mit einer Formatzeichenfolge? – Rory

+1

String.Format ({0} {1} {2}, "Format", "Zeichenfolge", "Beispiel."); –

0

Während Sie eine Enumeration verwenden können, um die Aktion anzugeben, die Sie ausführen möchten, oder CodeDom verwenden, um Code dynamisch zu verteilen, kommt es darauf an, dass Sie eine Transformation definieren möchten, was bedeutet, dass Sie Eingaben haben und eine Ausgabe.

Herauszufinden, die Ausgabe ist in diesem Fall einfach, Sie haben eine Zeichenfolge. Für die Eingaben scheint es, als könnten Sie eine variable Anzahl von Eingängen haben. Das würde als IEnumerable<string> definiert werden.

, die mit im Auge, können Sie eine Schnittstelle wie so definieren:

public interface IStringManipulation 
{ 
    string Manipulate(IEnumerable<string> parameters); 
} 

Dann wäre es einfach Implementierungen dieser Art zu definieren und dann die Typnamen in der Config platzieren.

Sie wirklich wollen dies anstelle von Code aus Strings dynamisch kompilieren. Bei der Verwendung von Zeichenfolgen haben Sie eine große Flexibilität, ja, aber Sie haben keine Kompilierzeitüberprüfung und öffnen sich für Fehler und Sicherheitsprobleme.

Auch die Zeit, die es dauern wird, um ein Stück Code zu schreiben, um Code auf der Basis des Stringfragments zu emittieren, das Sie bereitstellen, wird ziemlich langweilig auch sein, wie Sie die Baugruppe, die Klasse bauen müssen Methode, dann kompilieren, und dann rufen Sie die Methode, die Sie kompilieren dynamisch durch Reflexion (es sei denn, Sie implementieren es eine Schnittstelle, in diesem Fall können Sie auch tun, was ich vorschlagen sowieso).

+0

Ich weiß nicht im Voraus, welche Manipulationen notwendig sein könnten, und ich möchte keine zusätzlichen Typen kompilieren und bereitstellen, um diese Konfigurationsänderungen vorzunehmen (aufgrund meiner App-Architektur ist dies nicht möglich). Daher schätze ich die Konfigurierbarkeit von Strings über die beschriebenen Vorteile. – Rory

4

ich weiß, Sie sind nach C# zurückgibt, aber die Code, den ich dafür habe, ist in VB. Sie können es einfach mit dem VB-zu-C# -Konverter von Developer Fusion übersetzen. Ich habe es in einem Projekt verwendet, um es Benutzern zu ermöglichen, zur Laufzeit komplexe Berechnungen in ihre Anwendung einzufügen. Er kompiliert ihren VB-Code in eine Bibliothek im Speicher und führt dann den Code aus, der die resultierende Ausgabe zurückgibt. Es könnte relativ leicht für das, was Sie tun wollen, wieder verwendet werden.

Imports System.Reflection 
Imports System.CodeDom.Compiler 
Imports System.Text.RegularExpressions 
Imports System.Math 

Module Module1 

    Function Evaluate(ByVal Expression As String, ByVal Args() As Object) As Object 

    If Expression.Length > 0 Then 

     'Replace each parameter in the calculation expression with the correct values 
     Dim MatchStr = "{(\d+)}" 
     Dim oMatches = Regex.Matches(Expression, MatchStr) 
     If oMatches.Count > 0 Then 
      Dim DistinctCount = (From m In oMatches _ 
           Select m.Value).Distinct.Count 
      If DistinctCount = Args.Length Then 
       For i = 0 To Args.Length - 1 
        Expression = Expression.Replace("{" & i & "}", Args(i)) 
       Next 
      Else 
       Throw New ArgumentException("Invalid number of parameters passed") 
      End If 
     End If 

     Dim FuncName As String = "Eval" & Guid.NewGuid.ToString("N") 
     Dim FuncString As String = "Imports System.Math" & vbCrLf & _ 
            "Namespace EvaluatorLibrary" & vbCrLf & _ 
            " Class Evaluators" & vbCrLf & _ 
            " Public Shared Function " & FuncName & "() As Double" & vbCrLf & _ 
            "  " & Expression & vbCrLf & _ 
            " End Function" & vbCrLf & _ 
            " End Class" & vbCrLf & _ 
            "End Namespace" 

     'Tell the compiler what language was used 
     Dim CodeProvider As CodeDomProvider = CodeDomProvider.CreateProvider("VB") 

     'Set up our compiler options... 
     Dim CompilerOptions As New CompilerParameters() 
     With CompilerOptions 
      .ReferencedAssemblies.Add("System.dll") 
      .GenerateInMemory = True 
      .TreatWarningsAsErrors = True 
     End With 

     'Compile the code that is to be evaluated 
     Dim Results As CompilerResults = _ 
      CodeProvider.CompileAssemblyFromSource(CompilerOptions, FuncString) 

     'Check there were no errors... 
     If Results.Errors.Count > 0 Then 
     Else 
      'Run the code and return the value... 
      Dim dynamicType As Type = Results.CompiledAssembly.GetType("EvaluatorLibrary.Evaluators") 
      Dim methodInfo As MethodInfo = dynamicType.GetMethod(FuncName) 
      Return methodInfo.Invoke(Nothing, Nothing) 
     End If 

    Else 
     Return 0 

    End If 

    Return 0 

    End Function 

End Module 

ich meine dynamische Code wie folgt auf:

Dim Expr As String = " If ({0} < 20000) Then" & vbCrLf & _ 
        " Return Max(15, Min(75,0.12*{0}))" & vbCrLf & _ 
        " Else" & vbCrLf & _ 
        " Return Max(75,0.05*{0})" & vbCrLf & _ 
        " End If" 

Und dann einige Argumente für den Ausdruck einrichten und ausführen:

Dim Args As New List(Of String) 
While True 
    Dim Val As String = Console.ReadLine 
    Args.Clear() 
    If IsNumeric(Val) Then 
     Args.Add(Val) 
     Dim dblOut As Object = Evaluate(Expr, Args.ToArray) 
     Console.WriteLine(dblOut) 
    Else 
     Exit While 
    End If 
End While 
0

Ich lief nur den anderen Tag über etwas was genau das in einem anderen tut.NET-Sprache: http://reverseblade.blogspot.com/2009/02/dont-wait-for-c-5-use-nemerle.html

Ich könnte mir vorstellen, Sie könnten Ihre Zeichenfolge-Verarbeitung Code in Nemerle schreiben, kompilieren und Referenz von Ihrem C# oder VB-App.

Ich würde immer noch eine erweiterbare Design bevorzugen wie CasperOne suggeriert. Wenn Sie dynamisches Scripting durchführen, werden Sie lediglich die Kompilierung in die App pushen und die Bereitstellung in jedem beliebigen Prozess erhält die Programmierzeichenfolgen für die App. Aber es hört sich so an, als hätten Sie Ihre Gründe, also ist die einzige andere Sache, die ich hier in Betracht ziehen würde, die Sicherheit. Im Idealfall möchten Sie den Benutzer so stark wie möglich auf nur Manipulieren von Zeichenfolgen beschränken, in welchem ​​Fall List-of-Regex scheint eine ziemlich gute Option.

12

Es ist nicht zu schwer;) Ich habe ein kleines Beispiel zusammengestellt. Dies sollte Ihnen helfen zu entscheiden, ob Sie dynamische Skripte .. oder Regexes verwenden möchten.

Was Sie tun können, ist eine Schnittstelle in Ihrer Assembly erstellen, die Ihre dynamischen Code implementieren:

namespace CompileScriptExample 
{ 
    public interface IStringManipulator 
    { 
    string processString(string aString); 
    } 
} 

Dann eine Script Klasse erstellen:

namespace CompileScriptExample 
{ 
public class ScriptRunner 
{ 

    public static string RunScript(string scriptCode, string scriptParameter) 
    { 

     CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider(); 

     //configure parameters 
     CompilerParameters parameters = new CompilerParameters(); 
     parameters.GenerateExecutable = false; 
     parameters.GenerateInMemory = true; 
     parameters.IncludeDebugInformation = false; 
     string reference; 
     // Set reference to current assembly - this reference is a hack for the example.. 
     reference = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
     parameters.ReferencedAssemblies.Add(reference+"\\CompileScriptExample.exe"); 

     //compile 
     CompilerResults results = provider.CompileAssemblyFromSource(parameters, new string[] { scriptCode }); 

     if (results.Errors.Count == 0) 
     { 
      IStringManipulator compiledScript=(IStringManipulator)FindInterface(results.CompiledAssembly, "IStringManipulator"); 
      return compiledScript.processString(scriptParameter);//run the script, pass the string param.. 
     } 
     else 
     { 
      foreach(CompilerError anError in results.Errors) 
      { 
       MessageBox.Show(anError.ErrorText); 
      } 
      //handle compilation errors here 
      //..use results.errors collection 
      throw new Exception("Compilation error..."); 
     } 
    } 

    private static object FindInterface(Assembly anAssembly, string interfaceName) 
    { 
     // find our interface type.. 
     foreach (Type aType in anAssembly.GetTypes()) 
     { 
      if (aType.GetInterface(interfaceName, true) != null) 
       return anAssembly.CreateInstance(aType.FullName); 
     } 
     return null; 
    } 
} 

}

Jetzt alle Sie müssen eine Script-Zeichenfolge mit Code erstellen, der Ihre Schnittstelle wie folgt implementiert:

string [email protected]"using CompileScriptExample; 
public class MyStringManipulator : IStringManipulator 
{ 
    public string processString(string aString) 
    { 
     return aString+aString; 
    } 
}; 

und dann .. in Ihrem Code, die Verwendung des Script machen Ihre Saite mit dem dynamischen Code zu verarbeiten:

string processedString = ScriptRunner.RunScript(myScriptString, "hello"); 
+0

Ausgezeichnete Probe. – ewbi

+0

Arbeitende und saubere Probe. Ich bin beeindruckt. – BossRoss

0

Es gibt Keine eingebaute C# -Methode zum Aufrufen von eval() zur Laufzeit.

Allerdings ermöglicht mein C# eval-Programm die Auswertung von C# -Code. Es ermöglicht die Auswertung von C# -Code zur Laufzeit und unterstützt viele C# -Anweisungen. Tatsächlich ist dieser Code in jedem .NET-Projekt verwendbar, ist jedoch auf die Verwendung von C# -Syntax beschränkt. Weitere Informationen finden Sie auf meiner Website, http://csharp-eval.com.

Verwandte Themen