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)
我知道你在使用 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)
就像其他人已经提到的那样,实际上不可能在 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”