小虾9527

导航

 

在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类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"},
        };
    }
Models

测试代码:

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);
        }
Program

测试结果:

 

后来联系到设计模式的工厂模式,试想在项目中包含接口文件,而项目的实现类不包含在项目中,在某个指定的文件中,这样就可以随时修改实现类的文件而不需要重新编译整个项目,于是在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

posted on 2016-02-26 10:05  小虾9527  阅读(761)  评论(0编辑  收藏  举报