You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何将字符串表达式(如a + b * 2)解析为函数?

在C#里要把像a + b * 2这种字符串表达式转成可执行函数,有几种实用的方案,我给你逐个讲清楚:

方法一:用Roslyn表达式树(推荐,原生官方支持)

这是微软官方的编译器平台方案,稳定性和兼容性拉满,而且不需要额外的第三方依赖(除了一个轻量的NuGet包)。核心思路是把字符串表达式解析成表达式树,再编译成可直接调用的委托。

首先得安装NuGet包:Microsoft.CodeAnalysis.CSharp.Scripting

然后看代码示例:

using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

// 泛型方法,支持自定义参数类型和返回值类型
public static Func<T1, T2, TResult> CreateExpressionFunc<T1, T2, TResult>(string expression)
{
    // 配置脚本选项,添加必要的引用和命名空间
    var scriptOptions = ScriptOptions.Default
        .AddReferences(typeof(object).Assembly)
        .AddImports("System");

    // 用元组作为全局变量容器,对应表达式里的a、b参数
    var script = CSharpScript.Create<Func<T1, T2, TResult>>(
        $"return ({expression});",
        scriptOptions,
        globalsType: typeof((T1 a, T2 b)));

    // 编译并获取可执行委托
    var compiledFunc = script.RunAsync().Result.ReturnValue;
    return compiledFunc;
}

// 调用示例
var calculateFunc = CreateExpressionFunc<double, double, double>("a + b * 2");
double result = calculateFunc(3, 4); // 计算结果:3 + 4*2 = 11
Console.WriteLine(result);

如果你的表达式有更多参数,只需要扩展元组或者自定义一个类作为全局变量容器就行,灵活性很高。

方法二:动态编译成内存程序集

这个方案是把表达式包装成一个静态类的方法,编译成内存中的临时程序集,再通过反射获取方法并创建委托。优点是不需要额外的NuGet包(系统自带的类库就能实现),缺点是代码相对繁琐一点。

代码示例:

using System.CodeDom.Compiler;
using System.Reflection;
using System.Linq.Expressions;
using Microsoft.CSharp;

public static Delegate CreateCompiledFunction(string expression, Type[] paramTypes, Type returnType)
{
    // 生成参数名,比如p0、p1对应表达式里的a、b
    var paramNames = Enumerable.Range(0, paramTypes.Length)
        .Select(i => $"p{i}")
        .ToArray();
    // 生成方法的参数声明
    var paramDeclarations = string.Join(", ", paramNames.Select((name, idx) => $"{paramTypes[idx].FullName} {name}"));
    // 拼接完整的C#代码
    var codeSnippet = $@"
        using System;
        public static class ExprEvaluator
        {{
            public static {returnType.FullName} Calculate({paramDeclarations})
            {{
                return ({expression});
            }}
        }}";

    // 配置编译参数,生成内存程序集
    var compilerParams = new CompilerParameters
    {
        GenerateInMemory = true,
        TreatWarningsAsErrors = false
    };
    compilerParams.ReferencedAssemblies.Add("System.dll");

    // 编译代码
    var csharpProvider = new CSharpCodeProvider();
    var compileResult = csharpProvider.CompileAssemblyFromSource(compilerParams, codeSnippet);

    // 处理编译错误
    if (compileResult.Errors.HasErrors)
    {
        var errorMsg = string.Join("\n", compileResult.Errors.Cast<CompilerError>().Select(e => e.ErrorText));
        throw new InvalidOperationException($"编译失败:{errorMsg}");
    }

    // 获取编译后的方法并创建委托
    var assembly = compileResult.CompiledAssembly;
    var evaluateMethod = assembly.GetType("ExprEvaluator").GetMethod("Calculate");
    return Delegate.CreateDelegate(Expression.GetFuncType(paramTypes.Concat(new[] { returnType }).ToArray()), evaluateMethod);
}

// 调用示例
var compiledFunc = (Func<double, double, double>)CreateCompiledFunction(
    "a + b * 2", 
    new[] { typeof(double), typeof(double) }, 
    typeof(double));
double result = compiledFunc(3, 4); // 同样得到11
Console.WriteLine(result);
方法三:使用第三方表达式解析库(比如NCalc)

如果你的表达式比较复杂,或者不想折腾原生的编译逻辑,可以用专门的表达式解析库,比如NCalc。它轻量易用,支持运算符优先级、参数替换、甚至自定义函数,而且安全性更高(不会执行任意C#代码)。

首先安装NuGet包:NCalc

代码示例:

using NCalc;

public static Func<T1, T2, TResult> CreateNCalcFunc<T1, T2, TResult>(string expression)
{
    return (param1, param2) =>
    {
        var expr = new Expression(expression);
        // 设置表达式中的参数值
        expr.Parameters["a"] = param1;
        expr.Parameters["b"] = param2;
        // 执行表达式并返回结果
        return (TResult)expr.Evaluate();
    };
}

// 调用示例
var ncalcFunc = CreateNCalcFunc<double, double, double>("a + b * 2");
double result = ncalcFunc(3, 4); // 11
Console.WriteLine(result);
一些注意事项
  • 参数名对应:不管用哪种方案,表达式里的参数名(比如a、b)要和代码中设置的参数名一致,否则会报错;
  • 安全问题:如果表达式来自用户输入,要格外小心!Roslyn和动态编译方案会执行任意C#代码,存在注入风险。这种场景下优先用NCalc这类只解析表达式的库,或者对用户输入做严格的校验;
  • 性能差异:Roslyn表达式树编译后的委托调用性能和原生方法几乎一致;动态编译第一次有编译开销,后续调用很快;NCalc每次调用会解析表达式,性能稍弱但足够应对大多数业务场景。

内容的提问来源于stack exchange,提问作者Nikitc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:47:00