c#動態執行字串指令碼(最佳化版)

冰封一夏發表於2023-03-27

像javascript中有eval()來執行動態程式碼,c#中是沒有的,於是自己動手豐衣足食,

先來程式碼

c#動態執行字串指令碼(最佳化版)
  1 using System;
  2 using System.Data;
  3 using System.Configuration;
  4 using System.Text;
  5 using System.CodeDom.Compiler;
  6 using Microsoft.CSharp;
  7 using System.Reflection;
  8 using System.Collections.Generic;
  9 
 10 namespace 檢漏儀上位機
 11 {
 12     /// <summary>
 13     /// 本類用來將字串轉為可執行文字並執行
 14     /// 從別處複製,勿隨意更改!
 15     /// </summary>
 16     public class Evaluator
 17     {
 18         #region 建構函式
 19         /// <summary>
 20         /// 可執行串的建構函式
 21         /// </summary>
 22         /// <param name="items">
 23         /// 可執行字串陣列
 24         /// </param>
 25         public Evaluator(EvaluatorItem[] items, Dictionary<string, string> listAssemblies = null)
 26         {
 27             ConstructEvaluator(items, listAssemblies);      //呼叫解析字串建構函式進行解析
 28         }
 29         /// <summary>
 30         /// 可執行串的建構函式
 31         /// </summary>
 32         /// <param name="returnType">返回值型別</param>
 33         /// <param name="expression">執行表示式</param>
 34         /// <param name="name">執行字串名稱</param>
 35         public Evaluator(Type returnType, string expression, string name, Dictionary<string, string> listAssemblies = null)
 36         {
 37             //建立可執行字串陣列
 38             EvaluatorItem[] items = { new EvaluatorItem(returnType, expression, name) };
 39             ConstructEvaluator(items, listAssemblies);      //呼叫解析字串建構函式進行解析
 40         }
 41 
 42         public Evaluator(string allCode, string _namespace, string _class, List<string> listAssemblies = null)
 43         {
 44             ConstructEvaluatorByAllCode(allCode, _namespace, _class, listAssemblies);      //呼叫解析字串建構函式進行解析
 45         }
 46         /// <summary>
 47         /// 可執行串的建構函式
 48         /// </summary>
 49         /// <param name="item">可執行字串項</param>
 50         public Evaluator(EvaluatorItem item, Dictionary<string, string> listAssemblies = null)
 51         {
 52             EvaluatorItem[] items = { item };//將可執行字串項轉為可執行字串項陣列
 53             ConstructEvaluator(items, listAssemblies);      //呼叫解析字串建構函式進行解析
 54         }
 55         /// <summary>
 56         /// 解析字串建構函式
 57         /// </summary>
 58         /// <param name="items">待解析字串陣列</param>
 59         private void ConstructEvaluator(EvaluatorItem[] items, Dictionary<string, string> listAssemblies = null)
 60         {
 61 
 62             //建立C#編譯器例項
 63             ICodeCompiler comp = (new CSharpCodeProvider().CreateCompiler());
 64             //編譯器的傳入引數
 65             CompilerParameters cp = new CompilerParameters();
 66 
 67             cp.ReferencedAssemblies.Add("system.dll");              //新增程式集 system.dll 的引用
 68             cp.ReferencedAssemblies.Add("system.data.dll");         //新增程式集 system.data.dll 的引用
 69             cp.ReferencedAssemblies.Add("system.xml.dll");          //新增程式集 system.xml.dll 的引用
 70             cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");          //新增程式集 system.xml.dll 的引用
 71             cp.GenerateExecutable = false;                          //不生成可執行檔案
 72             cp.GenerateInMemory = true;                             //在記憶體中執行   
 73 
 74             StringBuilder code = new StringBuilder();               //建立程式碼串
 75             if (listAssemblies != null)
 76             {
 77                 foreach (var item in listAssemblies)
 78                 {
 79                     cp.ReferencedAssemblies.Add(item.Key);              //新增程式集 引用
 80                     code.Append("using "+item.Value + "; \n");
 81 
 82                 }
 83             }
 84             /*
 85              * 新增常見且必須的引用字串
 86              */
 87             code.Append("using System; \n");
 88             code.Append("using System.Data; \n");
 89             code.Append("using System.Data.SqlClient; \n");
 90             code.Append("using System.Data.OleDb; \n");
 91             code.Append("using System.Xml; \n");
 92             code.Append("using System.Windows.Forms; \n");
 93 
 94             code.Append("namespace EvalGuy { \n");                  //生成程式碼的名稱空間為EvalGuy,和本程式碼一樣   
 95 
 96             code.Append(" public class _Evaluator { \n");          //產生 _Evaluator 類,所有可執行程式碼均在此類中執行
 97             foreach (EvaluatorItem item in items)               //遍歷每一個可執行字串項
 98             {
 99                 code.AppendFormat("    public {0} {1}() ",          //新增定義公共函式程式碼
100                                 item.ReturnType == null ? "void" : item.ReturnType.Name,             //函式返回值為可執行字串項中定義的返回值型別
101                                   item.Name);                       //函式名稱為可執行字串項中定義的執行字串名稱
102                 code.Append("{ ");                                  //新增函式開始括號
103                 if (item.ReturnType != null)
104                     code.AppendFormat("return ({0});", item.Expression);//新增函式體,返回可執行字串項中定義的表示式的值
105                 else
106                 {
107                     code.Append(item.Expression);//新增函式體,返回可執行字串項中定義的表示式的值
108                 }
109                 code.Append("}\n");                                 //新增函式結束括號
110             }
111             code.Append("} }");                                 //新增類結束和名稱空間結束括號   
112 
113             //得到編譯器例項的返回結果
114             CompilerResults cr = comp.CompileAssemblyFromSource(cp, code.ToString());
115 
116             if (cr.Errors.HasErrors)                            //如果有錯誤
117             {
118                 StringBuilder error = new StringBuilder();          //建立錯誤資訊字串
119                 error.Append("編譯有錯誤的表示式: ");                //新增錯誤文字
120                 foreach (CompilerError err in cr.Errors)            //遍歷每一個出現的編譯錯誤
121                 {
122                     error.AppendFormat("{0}\n", err.ErrorText);     //新增進錯誤文字,每個錯誤後換行
123                 }
124                 throw new Exception("編譯錯誤: " + error.ToString());//丟擲異常
125             }
126             Assembly a = cr.CompiledAssembly;                       //獲取編譯器例項的程式集
127             _Compiled = a.CreateInstance("EvalGuy._Evaluator");     //透過程式集查詢並宣告 EvalGuy._Evaluator 的例項
128         }
129 
130         private void ConstructEvaluatorByAllCode(string allcode, string _namespace, string _class, List<string> listAssemblies)
131         {
132             if (listAssemblies == null)
133             {
134                 listAssemblies = new List<string>();
135             }
136             //建立C#編譯器例項
137             ICodeCompiler comp = (new CSharpCodeProvider().CreateCompiler());
138             //編譯器的傳入引數
139             CompilerParameters cp = new CompilerParameters();
140             if (!listAssemblies.Contains("system.dll"))
141             {
142                 listAssemblies.Add("system.dll");
143                 listAssemblies.Add("system.data.dll");
144                 listAssemblies.Add("system.xml.dll");
145             }
146             foreach (var item in listAssemblies)
147             {
148                 cp.ReferencedAssemblies.Add(item);
149             }
150 
151             cp.GenerateExecutable = false;                          //不生成可執行檔案
152             cp.GenerateInMemory = true;                             //在記憶體中執行   
153 
154 
155             //得到編譯器例項的返回結果
156             CompilerResults cr = comp.CompileAssemblyFromSource(cp, allcode);
157 
158             if (cr.Errors.HasErrors)                            //如果有錯誤
159             {
160                 StringBuilder error = new StringBuilder();          //建立錯誤資訊字串
161                 error.Append("編譯有錯誤的表示式: ");                //新增錯誤文字
162                 foreach (CompilerError err in cr.Errors)            //遍歷每一個出現的編譯錯誤
163                 {
164                     error.AppendFormat("{0}\n", err.ErrorText);     //新增進錯誤文字,每個錯誤後換行
165                 }
166                 throw new Exception("編譯錯誤: " + error.ToString());//丟擲異常
167             }
168             Assembly a = cr.CompiledAssembly;                       //獲取編譯器例項的程式集
169             _Compiled = a.CreateInstance($"{_namespace}.{_class}");     //透過程式集查詢並宣告 EvalGuy._Evaluator 的例項
170         }
171         #endregion
172 
173         #region 公有成員
174         /// <summary>
175         /// 執行字串並返回整型值
176         /// </summary>
177         /// <param name="name">執行字串名稱</param>
178         /// <returns>執行結果</returns>
179         public int EvaluateInt(string name)
180         {
181             return (int)Evaluate(name);
182         }
183         /// <summary>
184         /// 執行字串並返回字串型值
185         /// </summary>
186         /// <param name="name">執行字串名稱</param>
187         /// <returns>執行結果</returns>
188         public string EvaluateString(string name)
189         {
190             return (string)Evaluate(name);
191         }
192         /// <summary>
193         /// 執行字串並返回布林型值
194         /// </summary>
195         /// <param name="name">執行字串名稱</param>
196         /// <returns>執行結果</returns>
197         public bool EvaluateBool(string name)
198         {
199             return (bool)Evaluate(name);
200         }
201         /// <summary>
202         /// 執行字串並返 object 型值
203         /// </summary>
204         /// <param name="name">執行字串名稱</param>
205         /// <returns>執行結果</returns>
206         public object Evaluate(string name)
207         {
208             MethodInfo mi = _Compiled.GetType().GetMethod(name);//獲取 _Compiled 所屬型別中名稱為 name 的方法的引用
209             return mi.Invoke(_Compiled, null);                  //執行 mi 所引用的方法
210         }
211         #endregion
212 
213         #region 靜態成員
214         /// <summary>
215         /// 執行表示式並返回整型值
216         /// </summary>
217         /// <param name="code">要執行的表示式</param>
218         /// <param name="listAssemblies">需要引用到類庫,key:dll名稱,value:名稱空間名稱</param>
219         /// <returns>運算結果</returns>
220         static public int EvaluateToInteger(string code, Dictionary<string, string> listAssemblies = null)
221         {
222             Evaluator eval = new Evaluator(typeof(int), code, staticMethodName, listAssemblies);//生成 Evaluator 類的對像
223             return (int)eval.Evaluate(staticMethodName);                        //執行並返回整型資料
224         }
225         /// <summary>
226         /// 執行表示式並返回字串型值
227         /// </summary>
228         /// <param name="code">要執行的表示式</param>
229         /// <param name="listAssemblies">需要引用到類庫,key:dll名稱,value:名稱空間名稱</param>
230         /// <returns>運算結果</returns>
231         static public string EvaluateToString(string code, Dictionary<string, string> listAssemblies = null)
232         {
233             Evaluator eval = new Evaluator(typeof(string), code, staticMethodName, listAssemblies);//生成 Evaluator 類的對像
234             return (string)eval.Evaluate(staticMethodName);                     //執行並返回字串型資料
235         }
236         /// <summary>
237         /// 執行表示式並返回布林型值
238         /// </summary>
239         /// <param name="code">要執行的表示式</param>
240         /// <param name="listAssemblies">需要引用到類庫,key:dll名稱,value:名稱空間名稱</param>
241         /// <returns>運算結果</returns>
242         static public bool EvaluateToBool(string code, Dictionary<string, string> listAssemblies = null)
243         {
244             Evaluator eval = new Evaluator(typeof(bool), code, staticMethodName, listAssemblies);//生成 Evaluator 類的對像
245             return (bool)eval.Evaluate(staticMethodName);                       //執行並返回布林型資料
246         }
247         /// <summary>
248         /// 執行表示式並返回 object 型值
249         /// </summary>
250         /// <param name="code">要執行的表示式</param>
251         /// <param name="listAssemblies">需要引用到類庫,key:dll名稱,value:名稱空間名稱</param>
252         /// <returns>運算結果</returns>
253         static public object EvaluateToObject(string code, Dictionary<string, string> listAssemblies = null)
254         {
255             Evaluator eval = new Evaluator(typeof(object), code, staticMethodName, listAssemblies);//生成 Evaluator 類的對像
256             return eval.Evaluate(staticMethodName);                             //執行並返回 object 型資料
257         }
258         /// <summary>
259         /// 執行一個無返回式的程式碼
260         /// </summary>
261         /// <param name="code"></param>
262         /// <param name="listAssemblies">需要引用到類庫,key:dll名稱,value:名稱空間名稱</param>
263         static public void EvaluateToVoid(string code, Dictionary<string, string> listAssemblies = null)
264         {
265             Evaluator eval = new Evaluator(null, code, staticMethodName, listAssemblies);//生成 Evaluator 類的對像
266             eval.Evaluate(staticMethodName);                             //執行並返回 object 型資料
267         }
268         /// <summary>
269         /// 執行一個全程式碼
270         /// </summary>
271         /// <param name="code">全程式碼,包含名稱空間引用,名稱空間宣告,類宣告,函式宣告等</param>
272         /// <param name="_namespace"></param>
273         /// <param name="_class"></param>
274         /// <param name="methodName">函式</param>
275         /// <param name="listAssemblies">需要引用到類庫</param>
276         public static void EvaluateByAllCode(string code, string _namespace, string _class, string methodName, List<string> listAssemblies = null)
277         {
278             Evaluator eval = new Evaluator(code, _namespace, _class, listAssemblies);//生成 Evaluator 類的對像
279             eval.Evaluate(methodName);
280         }
281         #endregion
282 
283         #region 私有成員
284         /// <summary>
285         /// 靜態方法的執行字串名稱
286         /// </summary>
287         private const string staticMethodName = "__foo";
288         /// <summary>
289         /// 用於動態引用生成的類,執行其內部包含的可執行字串
290         /// </summary>
291         object _Compiled = null;
292         #endregion
293     }
294     /// <summary>
295     /// 可執行字串項(即一條可執行字串)
296     /// </summary>
297     public class EvaluatorItem
298     {
299         /// <summary>
300         /// 返回值型別
301         /// </summary>
302         public Type ReturnType;
303         /// <summary>
304         /// 執行表示式
305         /// </summary>
306         public string Expression;
307         /// <summary>
308         /// 執行字串名稱
309         /// </summary>
310         public string Name;
311         /// <summary>
312         /// 可執行字串項建構函式
313         /// </summary>
314         /// <param name="returnType">返回值型別</param>
315         /// <param name="expression">執行表示式</param>
316         /// <param name="name">執行字串名稱</param>
317         public EvaluatorItem(Type returnType, string expression, string name)
318         {
319             ReturnType = returnType;
320             Expression = expression;
321             Name = name;
322         }
323     }
324 }
View Code

呼叫一個無返回值的程式碼,顯示一個提示框

Evaluator.EvaluateToVoid("MessageBox.Show(\"Test\");",new Dictionary<string, string>() { { "System.Windows.Forms.dll", "System.Windows.Forms" } });

呼叫一個計算返回整型

            Evaluator.EvaluateToInteger("1+2*3");

呼叫一個全程式碼

            string str = @"using System;
namespace a
{ 
    public class b
    {
        public  void c()
        {
            Console.WriteLine(1);
        }
    }
}";

Evaluator.EvaluateByAllCode(str, "a", "b", "c");

功能就這麼多

 

相關文章