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

有没有办法使用.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. 

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

 // 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. 

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

更新 – 使用动态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 

然后我可以将任意sql放入我的@manipulation,类似这样的SET @result = REPLACE(REPLACE(@ s,’。’,’ – ‘),”,”)

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

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

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

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

像其他人已经提到的那样,它不可能在eval()函数中编译c#。 该function计划用于后来发布的clr,这是安德斯在PDC上演示的。

作为一个不同的解决方案,如果你的应用程序能够在单声道上运行,你可以使用它的eval函数,它可以动态编译c#代码,就像javascript一样。 它基本上已经做了.net将在一两年内完成的工作。

作为替代,如果你不能使用单声道,你可以编写在具有eval()的ironruby中进行字符串操作的部分。 你的其余代码甚至不知道你正在使用ruby进行那个类/ assambly。

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

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

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

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

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

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

然后创建一个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; } } 

}

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

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

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

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

我知道你是在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 

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

 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" 

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

 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 

我认为使用reflection.emit和codedom可以做到这一点,但它并非完全无关紧要,我建议反对它。

作为替代方案,您可以尝试配置格式字符串,可能除了正则表达式之外。

我的Eval解决方案怎么样?

如何在动态类型上执行字符串路径?

虽然您可以使用枚举来指示您想要采取的操作,或者使用CodeDom动态发出代码,但归结为您要定义某种转换,这意味着您有输入和输出。

在这种情况下,确定输出很容易,你有一个字符串。 对于输入,您似乎可以拥有可变数量的输入。 那将被定义为IEnumerable

考虑到这一点,您可以定义如下界面:

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

然后,很容易定义此类型的实现,然后将类型名称放在配置中。

真的想这样做,而不是动态编译字符串中的代码。 在使用字符串时,你有很大的灵活性,是的,但你没有编译时间检查,并且正在打开自己的bug和安全问题。

此外,基于您提供的字符串片段编写一段代码来发出代码所需的时间也将非常繁琐,因为您必须构建程序集,类,方法,然后编译,然后调用通过reflection动态编译的方法(除非你实现了一个接口,在这种情况下,你也可以做我建议的反正)。

我刚刚在另一天用另一种.NET语言运行了一些东西: http : //reverseblade.blogspot.com/2009/02/dont-wait-for-c-5-use-nemerle.html

我想你可以用你的C#或VB应用程序在nemerle,编译和引用中编写你的字符串处理代码。

我仍然赞成像casperOne建议的可扩展设计。 通过动态脚本编写,您只需将编译推送到应用程序并部署到任何进程获取应用程序的编程字符串。 但听起来你有理由,所以我在这里考虑的另一件事就是安全性。 理想情况下,您希望尽可能地限制用户操作字符串,在这种情况下,List-of-Regex似乎是一个非常好的选择。

在运行时调用eval()没有C#内置方法。

但是,我的C#eval程序允许评估C#代码。 它提供了在运行时评估C#代码并支持许多C#语句。 实际上,此代码可在任何.NET项目中使用,但是,它仅限于使用C#语法。 有关其他详细信息,请访问我的网站http://csharp-eval.com 。