Welcome

首页 / 软件开发 / C# / C#动态编译及实现按钮功能动态配置

C#动态编译及实现按钮功能动态配置2014-10-01 csdn博客 龙轩现在对做的系统要求要越来越灵活,功能配置越来越方便,牺牲一小部分的效率,而换取系统的灵活性,对于维护、功能扩展升级等工作提供了很大的方便。

前两天,一个项目要求界面上的按钮都是可以配置的,位置和功能都是可配置的。位置好说,用xml即可。但是功能可配置就有点难度了。如果说使用接口,那么参数则不好设置,而且就算用接口,在实际调用时,也得明确实例化哪个类。您可能还会说用反射,嗯,这的确是个好办法,但是还是在调用的时候,参数不确定,反射也就无用武之地了。查了半天,最终还是选择了动态编译。

用一个专门的类,完成动态编译的过程。其实这个动态编译,就是动态生成代码,经过动态编译,然后直接在系统中可以使用。所以需要你在代码中添加中功能所需要的动态链接库、程序集以及命名空间。以下是我用到的动态编译的类:

using System;using System.Data;using System.Configuration;using System.IO;using System.Text;using System.CodeDom.Compiler;using System.Windows.Forms;using Microsoft.CSharp;using System.Reflection;namespace DynamicAddFunction{/// <summary> /// 本类用来将字符串转为可执行文本并执行,用于动态定义按钮响应的事件。/// </summary> public class Evaluator{private string filepath = Path.Combine(Application.StartupPath, "FunBtn.config");#region 构造函数/// <summary> /// 可执行串的构造函数 /// </summary> /// <param name="items"> /// 可执行字符串数组 /// </param> public Evaluator(EvaluatorItem[] items){ConstructEvaluator(items);//调用解析字符串构造函数进行解析 }/// <summary> /// 可执行串的构造函数 /// </summary> /// <param name="returnType">返回值类型</param> /// <param name="expression">执行表达式</param> /// <param name="name">执行字符串名称</param> public Evaluator(Type returnType, string expression, string name){//创建可执行字符串数组 EvaluatorItem[] items = { new EvaluatorItem(returnType, expression, name) };ConstructEvaluator(items);//调用解析字符串构造函数进行解析}/// <summary> /// 可执行串的构造函数 /// </summary> /// <param name="item">可执行字符串项</param> public Evaluator(EvaluatorItem item){EvaluatorItem[] items = { item };//将可执行字符串项转为可执行字符串项数组 ConstructEvaluator(items);//调用解析字符串构造函数进行解析 }/// <summary> /// 解析字符串构造函数 /// </summary> /// <param name="items">待解析字符串数组</param> private void ConstructEvaluator(EvaluatorItem[] items){//创建C#编译器实例 //ICodeCompiler comp = (new CSharpCodeProvider().CreateCompiler());CSharpCodeProvider comp = new CSharpCodeProvider(); //编译器的传入参数 CompilerParameters cp = new CompilerParameters();Configer configer = Configer.Current(filepath);string[] assemblies = configer.GetAssembly("FunBtn//assembly//dll","name");cp.ReferencedAssemblies.AddRange(assemblies); //添加程序集集合//cp.ReferencedAssemblies.Add("system.dll");//添加程序集 system.dll 的引用 //cp.ReferencedAssemblies.Add("system.data.dll"); //添加程序集 system.data.dll 的引用 //cp.ReferencedAssemblies.Add("system.xml.dll");//添加程序集 system.xml.dll 的引用 //cp.ReferencedAssemblies.Add("system.windows.forms.dll");//cp.ReferencedAssemblies.Add("FunButton.dll");//cp.ReferencedAssemblies.Add("DynamicAddFunction.exe");cp.GenerateExecutable = false;//不生成可执行文件 cp.GenerateInMemory = true; //在内存中运行 StringBuilder code = new StringBuilder(); //创建代码串 /* * 添加常见且必须的引用字符串 *///获取引用的命名空间string[] usings = configer.GetAssembly("FunBtn//assembly//using", "name");foreach (var @using in usings){code.Append(@using+"
");//添加引用的命名空间}//code.Append("using System; 
");//code.Append("using System.Data; 
");//code.Append("using System.Data.SqlClient; 
");//code.Append("using System.Data.OleDb; 
");//code.Append("using System.Xml; 
");//code.Append("using FunButton; 
");//code.Append("using System.Windows.Forms; 
");//code.Append("using DynamicAddFunction; 
");code.Append("namespace EvalGuy { 
");//生成代码的命名空间为EvalGuy,和本代码一样 code.Append(" public class _Evaluator { 
");//产生 _Evaluator 类,所有可执行代码均在此类中运行 foreach (EvaluatorItem item in items) //遍历每一个可执行字符串项 {code.AppendFormat("public {0} {1}() ",//添加定义公共函数代码 item.ReturnType.Name.ToLower() , //函数返回值为可执行字符串项中定义的返回值类型 item.Name); //函数名称为可执行字符串项中定义的执行字符串名称 code.Append("{ ");//添加函数开始括号if (item.ReturnType.Name == "Void"){code.AppendFormat("{0};", item.Expression);//添加函数体,返回可执行字符串项中定义的表达式的值 }else{code.AppendFormat("return ({0});", item.Expression);//添加函数体,返回可执行字符串项中定义的表达式的值 }code.Append("}
"); //添加函数结束括号 }code.Append("} }"); //添加类结束和命名空间结束括号 //得到编译器实例的返回结果 CompilerResults cr = comp.CompileAssemblyFromSource(cp, code.ToString());if (cr.Errors.HasErrors)//如果有错误 {StringBuilder error = new StringBuilder();//创建错误信息字符串 error.Append("编译有错误的表达式: ");//添加错误文本 foreach (CompilerError err in cr.Errors)//遍历每一个出现的编译错误 {error.AppendFormat("{0}
", err.ErrorText); //添加进错误文本,每个错误后换行 }throw new Exception("编译错误: " + error.ToString());//抛出异常 }Assembly a = cr.CompiledAssembly; //获取编译器实例的程序集 _Compiled = a.CreateInstance("EvalGuy._Evaluator"); //通过程序集查找并声明 EvalGuy._Evaluator 的实例 }#endregion #region 公有成员/// <summary> /// 执行字符串并返回整型值 /// </summary> /// <param name="name">执行字符串名称</param> /// <returns>执行结果</returns> public int EvaluateInt(string name){return (int)Evaluate(name);}/// <summary> /// 执行字符串并返回字符串型值 /// </summary> /// <param name="name">执行字符串名称</param> /// <returns>执行结果</returns> public string EvaluateString(string name){return (string)Evaluate(name);}/// <summary> /// 执行字符串并返回布尔型值 /// </summary> /// <param name="name">执行字符串名称</param> /// <returns>执行结果</returns> public bool EvaluateBool(string name){return (bool)Evaluate(name);}/// <summary> /// 执行字符串并返 object 型值 /// </summary> /// <param name="name">执行字符串名称</param> /// <returns>执行结果</returns> public object Evaluate(string name){MethodInfo mi = _Compiled.GetType().GetMethod(name);//获取 _Compiled 所属类型中名称为 name 的方法的引用 return mi.Invoke(_Compiled, null);//执行 mi 所引用的方法 }public void EvaluateVoid(string name){MethodInfo mi = _Compiled.GetType().GetMethod(name);//获取 _Compiled 所属类型中名称为 name 的方法的引用 mi.Invoke(_Compiled, null);//执行 mi 所引用的方法 } #endregion #region 静态成员/// <summary> /// 执行表达式并返回整型值 /// </summary> /// <param name="code">要执行的表达式</param> /// <returns>运算结果</returns> static public int EvaluateToInteger(string code){Evaluator eval = new Evaluator(typeof(int), code, staticMethodName);//生成 Evaluator 类的对像 return (int)eval.Evaluate(staticMethodName);//执行并返回整型数据 }/// <summary> /// 执行表达式并返回字符串型值 /// </summary> /// <param name="code">要执行的表达式</param> /// <returns>运算结果</returns> static public string EvaluateToString(string code){Evaluator eval = new Evaluator(typeof(string), code, staticMethodName);//生成 Evaluator 类的对像 return (string)eval.Evaluate(staticMethodName); //执行并返回字符串型数据 }/// <summary> /// 执行表达式并返回布尔型值 /// </summary> /// <param name="code">要执行的表达式</param> /// <returns>运算结果</returns> static public bool EvaluateToBool(string code){Evaluator eval = new Evaluator(typeof(bool), code, staticMethodName);//生成 Evaluator 类的对像 return (bool)eval.Evaluate(staticMethodName); //执行并返回布尔型数据 }/// <summary> /// 执行表达式并返回 object 型值 /// </summary> /// <param name="code">要执行的表达式</param> /// <returns>运算结果</returns> static public object EvaluateToObject(string code){Evaluator eval = new Evaluator(typeof(object), code, staticMethodName);//生成 Evaluator 类的对像 return eval.Evaluate(staticMethodName); //执行并返回 object 型数据 }/// <summary> /// 执行表达式并返回 void 空值 /// </summary> /// <param name="code">要执行的表达式</param> static public void EvaluateToVoid(string code){Evaluator eval = new Evaluator(typeof(void), code, staticMethodName);//生成 Evaluator 类的对像 eval.EvaluateVoid(staticMethodName); //执行并返回 object 型数据 } #endregion #region 私有成员/// <summary> /// 静态方法的执行字符串名称 /// </summary> private const string staticMethodName = "ExecuteBtnCommand";/// <summary> /// 用于动态引用生成的类,执行其内部包含的可执行字符串 /// </summary> object _Compiled = null;#endregion}/// <summary> /// 可执行字符串项(即一条可执行字符串) /// </summary> public class EvaluatorItem{/// <summary> /// 返回值类型 /// </summary> public Type ReturnType;/// <summary> /// 执行表达式 /// </summary> public string Expression;/// <summary> /// 执行字符串名称 /// </summary> public string Name;/// <summary> /// 可执行字符串项构造函数 /// </summary> /// <param name="returnType">返回值类型</param> /// <param name="expression">执行表达式</param> /// <param name="name">执行字符串名称</param> public EvaluatorItem(Type returnType, string expression, string name){ReturnType = returnType;Expression = expression;Name = name;}}}
为了提高其灵活性,上面这个类添加的程序集和命名空间,以及调用功能的代码,都改成了读取xml格式的配置文件来获取。这样做理论上可以使得系统中可以添加任意.net制作的dll、exe等的功能。大大增强了系统的灵活性。