在Thomas的回答之后,实际上可以直接从C#访问(已弃用的)JScript库,这意味着您可以使用类似于JScript的eval
函数的等效项。
using Microsoft.JScript; // needs a reference to Microsoft.JScript.dll
using Microsoft.JScript.Vsa; // needs a reference to Microsoft.Vsa.dll
// ...
string expr = "7 + (5 * 4)";
Console.WriteLine(JScriptEval(expr)); // displays 27
// ...
public static double JScriptEval(string expr)
{
// error checking etc removed for brevity
return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString());
}
private static readonly VsaEngine _engine = VsaEngine.CreateEngine();
这是完全可能的。 CodeSnippetCompileUnit 类基本上就是这样实现的。我为您编写了一些示例代码,您需要包含以下命名空间:
这是代码:
string source = @"
class MyType
{
public static int Evaluate(<!parameters!>)
{
return <!expression!>;
}
}
";
string parameters = "int a, int b, int c";
string expression = "a + b * c";
string finalSource = source.Replace("<!parameters!>", parameters).Replace("<!expression!>", expression);
CodeSnippetCompileUnit compileUnit = new CodeSnippetCompileUnit(finalSource);
CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);
Type type = results.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");
// The first parameter is the instance to invoke the method on. Because our Evaluate method is static, we pass null.
int result = (int)method.Invoke(null, new object[] { 4, -3, 2 });
使用任何参数和表达式,您都可以创建一个通用的表达式求值器。
如果在results.CompiledAssembly中出现FileNotFoundException,则表示代码片段编译失败。
您可能还想查看System.CodeDom.CodeSnippetExpression类。它用于更具体地读取表达式,但是单独的表达式无法编译,因此您需要使用更多的CodeDom来构建一个可工作的类和方法。如果您希望能够以编程方式操作正在生成的类的类型,则这很有用。CodeSnippetCompileUnit非常适合一次生成整个工作类(也更简单),但要对其进行操作,则需要进行不方便的字符串操作。
class JsMath
{
static function Eval(expression : String) : double
{
return eval(expression);
};
}
将其编译成类库:
jsc /t:library JsMath.js
double result = JsMath.Eval(expression);
eval
函数,而无需中间的JScript编译步骤。有关详细信息,请参见我的答案。 - LukeHeval()
函数可能无法正常工作。 - Alejandro Cortes对我来说,Vici.Parser非常好用:在这里查看,它是我目前发现的最灵活的表达式解析器。
(我们使用它来设置“可读性强”的业务规则,并使用SQL服务器数据库提供数据)
有示例可用,并且开发者提供了非常好的支持(请查看网站论坛)。
static double Evaluate(string expression)
{
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof(double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double)(loDataTable.Rows[0]["Eval"]);
}
string finalSource = ...;
IEnumerable<Assembly> references = ...;
var compilation = CSharpCompilation.Create("Dynamic",
new[] {
SyntaxFactory.ParseSyntaxTree(
finalSource,
CSharpParseOptions.Default
.WithLanguageVersion(LanguageVersion.Latest)
) },
references.Select(a => MetadataReference.CreateFromFile(a.Location)),
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)
);
using var ms = new MemoryStream();
var e = compilation.Emit(ms);
if (!e.Success)
throw new Exception("Compilation failed");
ms.Seek(0, SeekOrigin.Begin);
var context = new AssemblyLoadContext(null, true);
var assembly = context.LoadFromStream(ms);
AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "netstandard").Single(),
typeof(object).Assembly
2.5+5.9
,17.89-2.47+7.16
,5/2/2+1.5*3+4.58
,带有括号的表达式(((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))
以及带有变量的表达式:var a = 6;
var b = 4.32m;
var c = 24.15m;
var engine = new ExpressionEvaluator();
engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});
你也可以将参数作为命名变量传递:
dynamic dynamicEngine = new ExpressionEvaluator();
var a = 6;
var b = 4.5m;
var c = 2.6m;
dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);
它支持 .Net Standard 2.0,因此可以从 .Net Core 以及 .Net Full Framework 项目中使用,并且不需要任何外部依赖。