在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类Expression,利用.net的动态编译技术实现,代码如下:
public class Expression { /// <summary> /// 执行一个表达式,或代码片段 /// </summary> /// <param name="expression">表达式或代码片段</param> /// <param name="parameters">表达式中的变量与实例的映射关系</param> /// <returns>执行结果</returns> public static object Eval(string expression, SortedDictionary<string, object> parameters = null) { try { parameters = parameters ?? new SortedDictionary<string, object>(); var objects = parameters.Values.ToList(); var types = objects.Select(x => x.GetType()).ToList(); var func = GetFunc(expression, parameters.Keys.ToList(), types); var funcTypes = types.ToList(); funcTypes.Add(typeof(object)); var type = Type.GetType("System.Func`" + funcTypes.Count); if (type == null) return null; type = type.MakeGenericType(funcTypes.ToArray()); var rst = type.InvokeMember("Invoke", BindingFlags.InvokeMethod, null, func, objects.ToArray()); return rst; } catch (Exception) { return null; } } private static object GetFunc(string expression, List<string> alias, List<Type> types) { string clsName = string.Format("Class{0}", Guid.NewGuid()).Replace("-", "_"); const string methodName = "ReturnFunc"; var strCode = CreateCode(clsName, methodName, expression, alias, types); CompilerResults result = Compile(strCode); Assembly assembly = result.CompiledAssembly; Type aType = assembly.GetType(string.Format("NSName.{0}", clsName)); MethodInfo method = aType.GetMethod(methodName); return method.Invoke(null, null); } private static string CreateCode(string clsName, string methodName, string expression, List<string> alias, List<Type> types) { var ts = string.Empty; if (types != null && types.Any()) { ts = string.Join(",", types.Select(x => x.FullName)) + ","; } ts += "System.Object"; var strAlias = string.Format("({0})", string.Join(",", alias)); return @" using System; using System.Linq; using System.Collections.Generic; namespace NSName { internal static class " + clsName + @" { public static Func<" + ts + @"> " + methodName + @"() { return " + strAlias + @" => " + expression + @"; } } }"; } private static CompilerResults Compile(string code, List<string> assemblyPaths = null) { var provider = new CSharpCodeProvider(); var parameter = new CompilerParameters(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); assemblyPaths = assemblyPaths ?? new List<string>(); assemblyPaths.AddRange(assemblies.Where(x => !x.IsDynamic).Select(x => x.Location)); foreach (var assemblyPath in assemblyPaths) { if (!string.IsNullOrWhiteSpace(assemblyPath) && !parameter.ReferencedAssemblies.Contains(assemblyPath)) parameter.ReferencedAssemblies.Add(assemblyPath); } parameter.GenerateExecutable = false; parameter.GenerateInMemory = true; //将你的式子放在这里 CompilerResults result = provider.CompileAssemblyFromSource(parameter, code); if (result.Errors.Count > 0) { var errorMsg = result.Errors.OfType<CompilerError>() .Aggregate(string.Empty, (current, error) => current + error.ToString()); throw new Exception(errorMsg); } return result; } }
测试准备代码:
public class Model1 { public int X = 200; public int Y = 10; public int Cal() { return X / Y; } } public class Model2 { public int Z = 100; public bool B = false; public List<int> List = new List<int> { 512,623,21,92,131,340,31 }; } public class Model3 { public Model3() { var s = string.Join(",",List); } public List<TObj> List = new List<TObj> { new TObj{Age = 32,Name = "Hol"}, new TObj{Age = 22,Name = "Luk"}, new TObj{Age = 14,Name = "Jim"}, new TObj{Age = 42,Name = "Tom"}, }; }
测试代码:
static void Main() { object result; var x = new Model1(); var y = new Model2(); var z = new Model3(); result = Expression.Eval("20 - 15"); Console.WriteLine(result); result = Expression.Eval("20 > 15"); Console.WriteLine(result); result = Expression.Eval("x.X / x.Y", new SortedDictionary<string, object> { { "x", x } }); Console.WriteLine(result); result = Expression.Eval("x.X + x.Cal()", new SortedDictionary<string, object> { { "x", x } }); Console.WriteLine(result); result = Expression.Eval("x.X + y.Z", new SortedDictionary<string, object> { { "x", x }, { "y", y } }); Console.WriteLine(result); result = Expression.Eval("x.X > x.Y && 20 > 15", new SortedDictionary<string, object> { { "x", x } }); Console.WriteLine(result); result = Expression.Eval("x.X > x.Y || y.B", new SortedDictionary<string, object> { { "x", x }, { "y", y } }); Console.WriteLine(result); result = Expression.Eval("x.X != y.Z", new SortedDictionary<string, object> { { "x", x }, { "y", y } }); Console.WriteLine(result); result = Expression.Eval("y.B ? (x.X - x.Y) : (x.X + x.Y)", new SortedDictionary<string, object> { { "x", x }, { "y", y } }); Console.WriteLine(result); result = Expression.Eval("y.List[4]", new SortedDictionary<string, object> { { "y", y } }); Console.WriteLine(result); result = Expression.Eval("y.List.Count", new SortedDictionary<string, object> { { "y", y } }); Console.WriteLine(result); result = Expression.Eval("{ y.List.Add(120); return y.List.Last(); }", new SortedDictionary<string, object> { { "y", y } }); Console.WriteLine(result); result = Expression.Eval("string.Join(\"\t\", y.List.OrderBy(i => i).Select(i => i.ToString()))", new SortedDictionary<string, object> { { "y", y } }); Console.WriteLine(result); result = Expression.Eval("z.List.Max(x=>x.Age)", new SortedDictionary<string, object> { { "z", z } }); Console.WriteLine(result); result = Expression.Eval("string.Join(\", \",z.List.Select(x=>x.Name));", new SortedDictionary<string, object> { { "z", z } }); Console.WriteLine(result); }
测试结果:
后来联系到设计模式的工厂模式,试想在项目中包含接口文件,而项目的实现类不包含在项目中,在某个指定的文件中,这样就可以随时修改实现类的文件而不需要重新编译整个项目,于是在Expression类中增加方法:
/// <summary> /// 从文件中加载指定接口的实现类的实例 /// </summary> /// <typeparam name="T">指定的接口的类型</typeparam> /// <param name="fileName">类型加载的源文件</param> /// <param name="assemblyPaths">加载类型依赖的程序集路径</param> /// <returns>实例</returns> public static T GetInterfaceInstence<T>(string fileName, List<string> assemblyPaths = null) { var baseType = typeof(T); if (!baseType.IsInterface || string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName)) { return default(T); } var strCode = string.Empty; using (var file = new StreamReader(fileName, Encoding.Unicode)) { strCode = file.ReadToEnd(); } CompilerResults result = Compile(strCode, assemblyPaths); Assembly assembly = result.CompiledAssembly; var ts = assembly.GetTypes().ToList(); Type aType = ts.FirstOrDefault(x => x.GetInterface(baseType.FullName) != null); if (aType == null || !aType.IsClass) { return default(T); } return (T)Activator.CreateInstance(aType); }
关于表达式的编译,后来在网上找到一个解决方案,ExpressionEvaluator.dll