C# 動態編譯簡介

語言: CN / TW / HK

C# 動態型別與動態編譯簡介

動態型別

動態編譯

CSScript

關於C#的動態型別與動態編譯的簡介,主要是一個Demo。

動態型別

關鍵字:dynamic

這裡有詳細的介紹:[C#基礎知識系列]專題十七:深入理解動態型別

動態型別的應用場景

可以減少強制轉換(強制轉換其實挺好的,讓程式猿清楚地指定自己做了什麼,不至於出錯時不知所措)

簡化反射的寫法。

與動態語言互動。

// Install-Package IronPython // 需要安裝此 Nuget包
// 引入動態型別之後
// 可以在C#語言中與動態語言進行互動
// 下面演示在C#中使用動態語言Python




ScriptEngine engine = Python.CreateEngine();




// 呼叫Python語言的print函式來輸出
engine.Execute("print 'Hello world'");




// 呼叫python求解漢羅塔問題
engine.Execute(PythonCode1());




// 呼叫python進行計算,返回 dynamic 型別
dynamic result = engine.Execute("123+456");




public static string PythonCode1()
{
string code = "count=0\n" +
"def move(n,A,B,C):\n" +
" global count\n" +
" if(n==1):\n" +
" print(A+\"->\"+C)\n" +
" count = count + 1\n" +
" return\n" +
" move(n - 1, A, C, B)\n" +
" move(1, A, B, C)\n" +
" move(n - 1, B, A, C)\n" +
" return\n" +
"move(5,\"A\",\"B\",\"C\")";
return code;
}




動態編譯

javascript 和 matlab 等指令碼語言會有 eval 這個函式,可以將一些動態生成的字串作為程式碼執行,某些情況下很實用。

C#同樣支援動態編譯。

最主要的兩個類:

CodeDomProvider 和 CompilerParameters

前者相當於編譯器,後者相當於編譯器引數。

public static void Test1()
{
CodeDomProvider compiler = new CSharpCodeProvider(); //編譯器
CompilerParameters comPara = new CompilerParameters(); //編譯器引數




comPara.GenerateExecutable = true; // 生成exe檔案
comPara.GenerateInMemory = false; // 是否在記憶體在輸出
comPara.OutputAssembly = "SimpleCompile.exe"; // 輸出檔案




compiler.CompileAssemblyFromSource(comPara, GetCode1());
// 在當前目錄生成 SimpleCompile.exe ,可直接執行
}
public static string GetCode1()
{
string code = @"using System;
class Test
{
static void Main()
{
Console.WriteLine(""Hello world"");
Console.ReadLine();
}
}";
return code;
}

詳細下介紹可以看這裡:

C# 動態編譯

.NET Framework 中的動態程式設計

public static void Test2()
{
CodeDomProvider compiler = new CSharpCodeProvider(); //編譯器
CompilerParameters comPara = new CompilerParameters(); //編譯器引數




comPara.GenerateExecutable = false;
comPara.GenerateInMemory = true;




// GetCode2() 見文末
CompilerResults compilerResults = compiler.CompileAssemblyFromSource(comPara, GetCode2());




if (compilerResults.Errors.HasErrors)
{
Console.WriteLine("編譯錯誤");
foreach (CompilerError err in compilerResults.Errors)
{
Console.WriteLine(err.ErrorText);
}
return;
}




// 通過反射,呼叫HelloWorld的例項
Assembly objAssembly = compilerResults.CompiledAssembly;
object objHelloWorld = objAssembly.CreateInstance("DynamicCodeGenerate.HelloWorld");
MethodInfo objMi = objHelloWorld?.GetType().GetMethod("OutPut");
var result = objMi?.Invoke(objHelloWorld, null);




Console.WriteLine(result);




// 動態型別呼叫
// 可以看到,動態呼叫比反射呼叫寫法簡介不少
dynamic dynObj = objAssembly.CreateInstance("DynamicCodeGenerate.HelloWorld");
var result2 = dynObj?.OutPut();




Console.WriteLine(result2);
}




CSScript

CSScript是C#的一個動態編譯引擎。

簡介看這裡:

C#動態編譯引擎-CS-Script

CSScript.Net指令碼概述

Nuget

Github

如果用原生的動態編譯,每次都要生成一個程式集,然後通過反射的方式去呼叫,過於麻煩。

如果只是想動態編譯一句程式碼,CSScript提供了一種特別方便的寫法。

var sqr = CSScript.Evaluator
.CreateDelegate(@"int Sqr(int a)
{
return a * a;
}");




var r = sqr(3); // 計算3的平方

使用 CS-Script 需要安裝相關Nuget包 (會安裝很多東西,依賴項很多)

Install-Package CS-Script

Scripting.evaluator.cs

Scripting.Extensions.cs

Scripting.native.cs

這三個檔案是 Nuget 安裝 CS-Script 之後自動載入的,裡面有很多實用的例子~

有了CSScript,對原有的動態編譯的呼叫也變得簡單。

public static void Test1()
{
// 得到 Assembly,反射呼叫
Assembly ass = CSScript.LoadCode(GetCode2()); // GetCode2()見文末
AsmHelper assAsmHelper = new AsmHelper(ass);
object obj = assAsmHelper.CreateObject("DynamicCodeGenerate.HelloWorld");
var method = assAsmHelper.GetMethod(obj, "OutPut");
object result = method.Invoke();




// 動態呼叫
dynamic obj2 = CSScript.Evaluator.LoadCode(GetCode2());
dynamic result2 = obj2.OutPut();
}

關於CSScript的效能問題,可以參看這裡:C#指令碼引擎 CS-Script 之(二)——效能評測

public static string GetCode2()
{
StringBuilder sb = new StringBuilder();
sb.Append("using System;");
sb.Append(Environment.NewLine);
sb.Append("namespace DynamicCodeGenerate");
sb.Append(Environment.NewLine);
sb.Append("{");
sb.Append(Environment.NewLine);
sb.Append(" public class HelloWorld");
sb.Append(Environment.NewLine);
sb.Append(" {");
sb.Append(Environment.NewLine);
sb.Append(" public string OutPut()");
sb.Append(Environment.NewLine);
sb.Append(" {");
sb.Append(Environment.NewLine);
sb.Append(" return \"Hello world!\";");
sb.Append(Environment.NewLine);
sb.Append(" }");
sb.Append(Environment.NewLine);
sb.Append(" }");
sb.Append(Environment.NewLine);
sb.Append("}");




string code = sb.ToString();




return code;
}

往期 精彩 回顧

【推薦】.NET Core開發實戰視訊課程   ★★★

.NET Core實戰專案之CMS 第一章 入門篇-開篇及總體規劃

【.NET Core微服務實戰-統一身份認證】開篇及目錄索引

Redis基本使用及百億資料量中的使用技巧分享(附視訊地址及觀看指南)

.NET Core中的一個介面多種實現的依賴注入與動態選擇看這篇就夠了

10個小技巧助您寫出高效能的ASP.NET Core程式碼

用abp vNext快速開發Quartz.NET定時任務管理介面

在ASP.NET Core中建立基於Quartz.NET託管服務輕鬆實現作業排程

現身說法:實際業務出發分析百億資料量下的多表查詢優化

關於C#非同步程式設計你應該瞭解的幾點建議

C#非同步程式設計看這篇就夠了