有没有办法在.net中动态执行字符串,类似于javascript中的eval()或sql中的动态sql?

Ror*_*ory 8 .net c# string dynamic

有没有办法使用.net 2.0动态执行字符串中包含的代码,方式与javascript中的eval()或tsql中的sp_executeSQL类似?

我在一个变量中有一个字符串值,我想在我的应用程序中的某个点操作 - 所以代码基本上是字符串操作.我不知道需要什么样的操作,所以我希望它们是可配置的.

我真的不关心动态代码编写的语言,无论是最容易实现还是编写简单.

例如,我可能想要替换'.'的实例.带有' - '的字符,或删除所有空格或类似字符.如果我在sql中这样做我会使用动态sql,但我想在.net代码中执行它,如下所示:

// 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.
Run Code Online (Sandbox Code Playgroud)

我可以使用正则表达式查找/替换表达式.因为我正在做的只是字符串操作,所以这应该是足够的,只要我能写出足够聪明的正则表达式.例如:

// 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.
Run Code Online (Sandbox Code Playgroud)

但在某些情况下,我的操作要求可能会超出正则表达式的可能性,或者我可能想要应用多个步骤,例如替换'.' 用' - '也可以去掉空格.也许我可以存储一个查找/替换正则表达式的列表并迭代它们......但是有人有更好的建议吗?

更新 - 使用动态sql的示例

我不想要一个解决方案,要求我事先知道什么是可能的操作,我真的在寻找简单的东西.例如在sql中我会做这样的事情:

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
Run Code Online (Sandbox Code Playgroud)

然后我可以将任意sql放入我的@manipulation,类似这样的SET @result = REPLACE(REPLACE(@ s,'.',' - '),'','')

是的,这需要我注意允许将哪些值放入@manipulation,但它会给我以后需要的灵活性.

使用eval()可以在javascript中使用类似的方法.

更新 - 使用.net中的MSScript控件的示例:

似乎是一种可能的方法,虽然对于我想要处理的简单案例可能有些过分.它使用Microsoft Script Control库来允许执行任意VBScript.

mar*_*rkt 13

这不太难;)我把一个小例子放在一起.这应该可以帮助您决定是否要使用动态脚本..或正则表达式.

您可以做的是在程序集中创建一个接口,动态代码将实现该接口:

namespace CompileScriptExample
{
  public interface IStringManipulator
  {
    string processString(string aString);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个ScriptRunner类:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

}

现在,您所要做的就是创建一个脚本字符串,其中包含实现您的界面的代码,例如..

string myScriptString=@"using CompileScriptExample;
public class MyStringManipulator : IStringManipulator
{
  public string processString(string aString)
  {
        return aString+aString;
  }
};
Run Code Online (Sandbox Code Playgroud)

然后..在您的代码中,使用ScriptRunner处理您的动态代码字符串:

string processedString = ScriptRunner.RunScript(myScriptString, "hello");
Run Code Online (Sandbox Code Playgroud)


Ben*_*ter 5

我知道你在使用 C#,但我的代码是用 VB 编写的。您可以使用 Developer Fusion 的 VB 到 C# 转换器轻松地将其转换。我在一个项目中使用了它,以允许用户在运行时将复杂的计算添加到他们的应用程序中。它将他们的 VB 代码编译到内存中的库中,然后运行返回结果输出的代码。它可以很容易地重新用于您尝试做的事情。

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
Run Code Online (Sandbox Code Playgroud)

我像这样设置我的动态代码:

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"
Run Code Online (Sandbox Code Playgroud)

然后为表达式设置一些参数并执行:

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
Run Code Online (Sandbox Code Playgroud)


LDo*_*ala 1

就像其他人已经提到的那样,实际上不可能在 eval() 函数中编译 c#。该功能计划用于 clr 的最新版本,anders 在 PDC 上进行了演示。

作为一个不同的解决方案,如果您的应用程序能够在 Mono 上运行,您可以使用它的 eval 函数,它可以动态编译 C# 代码,就像 javascript 一样。它基本上已经做到了 .net 一两年内能够做到的事情。

作为替代方案,如果您不能使用 mono,您可以编写在具有 eval() 的 IronRuby 中执行字符串操作的部分。你的其余代码甚至不知道你正在为该类/组件使用 ruby​​。

对于这样一个简单的用例,您在更新中发布的链接看起来相当复杂。使用ironruby,您所要做的就是编写MyDynamicEvalClass,如下所示:

class MyDynamicEvalClass
  def eval(someString,transformString)
    eval(someString,transformString)
  end
end
Run Code Online (Sandbox Code Playgroud)

并用一些返回新字符串的 ruby​​ 代码替换“ManipulationSetting”