程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#動態編譯及實現按鈕功能動態配置

C#動態編譯及實現按鈕功能動態配置

編輯:關於C#

現在對做的系統要求要越來越靈活,功能配置越來越方便,犧牲一小部分的效率,而換取系統的靈活性,對於維護、功能擴展升級等工作提供了很大的方便。

前兩天,一個項目要求界面上的按鈕都是可以配置的,位置和功能都是可配置的。位置好說,用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+"\n");//添加引用的命名空間  
            }  
            //code.Append("using System; \n");  
            //code.Append("using System.Data; \n");  
            //code.Append("using System.Data.SqlClient; \n");  
            //code.Append("using System.Data.OleDb; \n");  
            //code.Append("using System.Xml; \n");  
            //code.Append("using FunButton; \n");  
            //code.Append("using System.Windows.Forms; \n");  
            //code.Append("using DynamicAddFunction; \n");  
      
            code.Append("namespace EvalGuy { \n");                  //生成代碼的命名空間為EvalGuy,和本代碼一樣     
      
            code.Append(" public class _Evaluator { \n");          //產生 _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("}\n");                                 //添加函數結束括號     
            }  
            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}\n", 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等的功能。大大增強了系統的靈活性。

讀寫配置文件的類采用了單例模式,可以減少對系統的消耗,代碼如下:

using System;  
using System.Drawing;  
using System.IO;  
using System.Windows.Forms;  
using System.Xml;  
using System.Reflection;  
using System.Configuration;  
      
namespace DynamicAddFunction  
{  
    ///   <summary>   
    ///   負責讀寫應用程序配置文件,即app.config的讀寫。   
    ///   </summary>   
    public class Configer  
    {  
        #region   私有成員  
      
        private string version; //版本號  
        private string updatetime; //更新時間  
        private string isautoupdate; //是否自動更新  
        private string funbtnwidth; //功能區按鈕寬度  
        private string funbtnheight; //功能區按鈕高度  
        private string expandbtnwidth; //擴展區按鈕寬度  
        private string expandbtnheight; //擴展區按鈕高度  
      
        //private string btnname; //按鈕名稱  
        //private string btntext; //按鈕顯示文本  
        //private string btntype; //按鈕類型  
        //private string btnleft; //左邊距  
        //private string btntop; //上邊距  
        //private string btnreturntype; //返回值類型  
        //private string btncode; //調用代碼  
      
        private string arrange; //擴展區按鈕布局  
        private string firstleft; //第一個按鈕的左邊距  
        private string firsttop; //第一個按鈕的上邊距  
        private string horizontal; //水平間距  
        private string vertical; //垂直間距  
      
        private string filePath; //文件路徑  
        private static Configer current; //唯一實例  
     
        #endregion  
      
        /// <summary>  
        /// 因為不需要多個實例,所以采用了單例模式,由Current屬性來獲取唯一的實例
	/// 查看本欄目
		

<?xml version="1.0" encoding="utf-8"?>  
<FunBtn>  
  <!-- 版本信息 -->
  <info version="1.00" updatetime="2013-01-01 10:00:00.123" isautoupdate="true"  />  
  <!-- 引用的程序集和命名空間 -->
  <assembly>  
    <dll name="system.dll"  />  
    <dll name="system.data.dll"  />  
    <dll name="system.xml.dll"  />  
    <dll name="system.windows.forms.dll"  />  
    <dll name="FunButton.dll"  />  
    <dll name="DynamicAddFunction.exe"  />  
    <using name="using System;"  />  
    <using name="using System.Data;"  />  
    <using name="using System.Data.SqlClient;"  />  
    <using name="using System.Data.OleDb;"  />  
    <using name="using System.Xml;"  />  
    <using name="using FunButton;"  />  
    <using name="using System.Windows.Forms;"  />  
    <using name="using DynamicAddFunction;"  />  
  </assembly>  
  <!-- 按鈕屬性和方法 -->
  <btndetail>  
    <btn name="btnDelOrder" value="刪除訂單" type="funbtn" rank="0" left="10" top="5" returntype="void" code="new DelOrderBtn().DelOrder(Form1.objs)"  />  
    <btn name="btnCheckOut" value="結 賬" type="funbtn" rank="-1" left="10" top="335" returntype="void" code="new CheckOutBtn().CheckOut(Form1.objs)"  />  
    <btn name="btnClose" value="退出" type="funbtn" rank="-1" left="240" top="335" returntype="void" code="Application.Exit()"  />  
    <btn name="btnDelAllOrder" value="整單刪除" type="funbtn" rank="-1" left="240" top="225" returntype="void" code="new DelOrderBtn().DelOrder(Form1.objs)"  />  
    <btn name="btnMore" value="更 多" type="funbtn" rank="6" left="10" top="230" returntype="void" code="new frmMore().Show()"  />  
  </btndetail>  
</FunBtn>

這樣在系統調用的時候就可以方便多了(我這裡所有的按鈕都相應一個事件,即Button_Click事件,它們是通過name,在配置文件中找到對應的代碼,然後動態編譯,然後去執行的)

/// <summary>  
/// 動態執行按鈕點擊事件  
/// </summary>  
public void Button_Click(object sender, EventArgs e)  
{  
    try
    {  
        Configer configer = Configer.Current(filePath);  
      
        //獲取調用的代碼
	// 查看本欄目
		
							
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved