C# 編譯器 和 反編譯器,你要哪一個(歪頭)? 我全都要(捏拳)!

前言

       從 C# 6.0 開始,C# 編譯器就從之前由 C++ 實現的 csc.exe 換成了用 C# 從新實現的開放式 API 式編譯服務 Roslyn。這個編譯器到如今已經替代了老式編譯器,從前 WebForm 的動態編譯基礎 CSharpCodeProvider 就是對 csc.exe 的調用包裝,固然也繼承了 csc.exe 的全部毛病。好比這是一個徹底的黑盒編譯器,把源碼傳進去返回程序集,中間的符號解析,詞法、語法分析,優化和生成這些中間步驟究竟發生了什麼咱們無從知曉。沒法做爲腳本引擎把 C# 代碼做爲腳本執行,曾經有過第三方腳本引擎。固然還有不支持 C# 6.0 或更高版本源代碼的編譯,巨硬是鐵了心要強推 Roslyn 逼死 csc.exe 啊,雖然都 9102 年了,csc.exe 早都透心涼了。html

       到 VS 2019,C# 又多了一個功能,導航到反編譯的源代碼,雖然以前已經有各類第三方反編譯工具和 VS 集成擴展,但總歸沒有自帶的來的方便。以前大名鼎鼎的 dotPeek 也在使用中發現沒法反編譯框架爲 .NetStandard 2.1 和 .NetCoreApp 3.1 的程序集,不知道如今怎麼樣了。恰好在使用中發現了 「由 ICSharpCode.Decompiler 提供反編譯服務」字樣,抱着好玩的心態去 Nuget 搜索了一下,竟然真的找到了!既然這樣,那豈不是能:把 Roslyn 編譯的程序集交給 ICSharpCode.Decompiler 反編譯的源代碼交給 Roslyn 編譯的程序集交給 ICSharpCode.Decompiler 反編譯的源代碼交給…… (→_→)git

正文

       這兩個包的基本使用其實很簡單。在項目的管理 Nuget 程序包中搜索並安裝 ICSharpCode.Decompiler 和 Microsoft.CodeAnalysis.CSharp.Scripting。github

       一、Microsoft.CodeAnalysis.CSharp.Scripting 基本使用:緩存

          引用命名空間 using Microsoft.CodeAnalysis.CSharp.Scripting;框架

          開始使用:dom

1 //執行表達式,返回強類型結果
2 int result = await CSharpScript.EvaluateAsync<int>("1 + 2");

          就這麼簡單,已經能執行一個簡單的表達式了,連 C# 語句要分號這種規則都不用 \(@^0^@)/ide

          其餘各類花式用法:要引用更多命名空間,包括但不限於工具

1 using Microsoft.CodeAnalysis;
2 using Microsoft.CodeAnalysis.CSharp;
3 using Microsoft.CodeAnalysis.CSharp.Scripting;
4 using Microsoft.CodeAnalysis.Scripting;
 1             //處理編譯錯誤
 2             try
 3             {
 4                 Console.WriteLine(await CSharpScript.EvaluateAsync("2+"));
 5             }
 6             catch (CompilationErrorException e)
 7             {
 8                 Console.WriteLine("2+ : " + string.Join(Environment.NewLine, e.Diagnostics));
 9             }
10 
11             //添加程序集引用
12             var result1 = await CSharpScript.EvaluateAsync("System.Net.Dns.GetHostName()",
13                 ScriptOptions.Default.WithReferences(typeof(System.Net.Dns).Assembly));
14             Console.WriteLine("System.Net.Dns.GetHostName() : " + result1);
15 
16             //導入命名空間 using
17             var result2 = await CSharpScript.EvaluateAsync("Directory.GetCurrentDirectory()",
18                 ScriptOptions.Default.WithImports("System.IO"));
19             Console.WriteLine("Directory.GetCurrentDirectory() : " + result2);
20 
21             //導入靜態類型 using static
22             var result3 = await CSharpScript.EvaluateAsync("Sqrt(2)",
23                 ScriptOptions.Default.WithImports("System.Math"));
24             Console.WriteLine("Sqrt(2) : " + result3);
25 
26             //參數化腳本
27             var globals = new Globals {X = 1, Y = 2};
28             Console.WriteLine("X + Y : " + await CSharpScript.EvaluateAsync<int>("X+Y", globals: globals));
29 
30             //編譯緩存並屢次執行腳本
31             var script = CSharpScript.Create<int>("X*Y", globalsType: typeof(Globals));
32             script.Compile();
33             for (int i = 0; i < 10; i++)
34             {
35                 Console.WriteLine("No " + (i + 1) + " X*Y : " + (await script.RunAsync(new Globals { X = i, Y = i })).ReturnValue);
36             }
37 
38             //編譯腳本爲委託
39             script = CSharpScript.Create<int>("X/Y", globalsType: typeof(Globals));
40             ScriptRunner<int> runner = script.CreateDelegate();
41             for (int i = 1; i < 11; i++)
42             {
43                 Console.WriteLine("No " + (i + 1) + " X/Y : " + await runner(new Globals { X = new Random().Next(1,i), Y = new Random().Next(1, i) }));
44             }
45 
46             //運行腳本片斷並檢查已定義的變量
47             var state = await CSharpScript.RunAsync<int>("int answer = 42;");
48             foreach (var variable in state.Variables)
49                 Console.WriteLine($"{variable.Name} = {variable.Value} of type {variable.Type}");
50 
51             //鏈接多個片斷爲一個腳本
52             var script1 = CSharpScript.
53                 Create<int>("int x = 1;").
54                 ContinueWith("int y = 2;").
55                 ContinueWith("x + y");
56             Console.WriteLine("x + y : " + (await script1.RunAsync()).ReturnValue);
57             //獲取編譯對象以訪問全部Roslyn API
58             //var compilation = script1.GetCompilation();
59 
60             //從以前的狀態繼續執行腳本
61             var state1 = await CSharpScript.RunAsync("int x = 1;");
62             state1 = await state1.ContinueWithAsync("int y = 2;");
63             state1 = await state1.ContinueWithAsync("x+y");
64             Console.WriteLine("x + y : " + state1.ReturnValue);
65 
66             //讀取代碼文件並執行編譯
67             var file = @"C:\Users\Administrator\source\repos\ConsoleApp2\ConsoleApp2/GenericGenerator.cs";
68             var originalText = File.ReadAllText(file);
69             var syntaxTree = CSharpSyntaxTree.ParseText(originalText);//獲取語法樹
70             var type = CompileType("GenericGenerator", syntaxTree);//執行編譯並獲取類型
71 
72             var transformer = Activator.CreateInstance(type);
73             var newContent = (string)type.GetMethod("Transform").Invoke(transformer,
74                 new object[] { "某個泛型類的全文,僞裝我是泛型類 Walterlv<T> is a sb.", 2 });
75 
76             var aa = new GenericGenerator();
77             var str = aa.Transform("某個泛型類的全文,僞裝我是泛型類 Walterlv<T> is a sb.", 25);
View Code
 1         private static Type CompileType(string originalClassName, SyntaxTree syntaxTree)
 2         {
 3             // 指定編譯選項。
 4             var assemblyName = $"{originalClassName}.g";
 5             var compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree },
 6                     options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
 7                 .AddReferences(
 8                     // 這算是偷懶了嗎?我把 .NET Core 運行時用到的那些引用都加入到引用了。
 9                     // 加入引用是必要的,否則連 object 類型都是沒有的,確定編譯不經過。
10                     AppDomain.CurrentDomain.GetAssemblies().Where(x=>!string.IsNullOrEmpty(x.Location)).Select(x => MetadataReference.CreateFromFile(x.Location)));
11 
12             // 編譯到內存流中。
13             using (var ms = new MemoryStream())
14             {
15                 var result = compilation.Emit(ms);
16 
17                 if (result.Success)
18                 {
19                     ms.Seek(0, SeekOrigin.Begin);
20                     var assembly = Assembly.Load(ms.ToArray());
21                     return assembly.GetTypes().First(x => x.Name == originalClassName);
22                 }
23                 throw new Exception(string.Join(Environment.NewLine, result.Diagnostics));
24             }
25         }
View Code

          固然還有這種四不像代碼也是能夠滴優化

 1 //不只能夠在腳本引擎中引用命名空間,還能夠在腳本中引用,固然前提是在腳本引擎中引用了相關程序集
 2 using System;
 3 
 4 //沒有 namespace Xxx { /* code */ } 的定義,直接在外面定義一個類
 5 public class Point
 6 {
 7     public int X {get; set;}
 8     public int Y {get; set;}
 9 
10     public Point(int x, int y)
11     {
12         X = x;
13         Y = y;
14     }
15 
16     public override string ToString()
17     {
18         return $"({X}, {Y})";
19     }
20 }
21 
22 //變量沒有定義在任何類或類的方法中,正常的 C# 代碼必定會報錯,但騷斷腿的 CSharpScript.EvaluateAsync<TResult>(string code) 能正常執行
23 var random = new Random();
24 
25 int a = random.Next(0, 1000);
26 int b = random.Next(0, 1000);
27 Func<int, int, int> plus = (l, r) => l + r;
28 //最後這個表達式沒有分號結尾,也沒有使用變量接收結果。固然,最後這個帶返回值的表達式的結果會在 string result = await CSharpScript.EvaluateAsync<string>(code); 的 result 裏收到
29 $"{a}+{b}={plus(a, b)}\r\nPoint : {new Point(random.Next(0, 1000), random.Next(0, 1000))}"

 

       二、ICSharpCode.Decompiler 的基本使用網站

1 using ICSharpCode.Decompiler;
2 using ICSharpCode.Decompiler.CSharp;
3 using ICSharpCode.Decompiler.TypeSystem;
4 
5 var decompiler = new CSharpDecompiler(typeof(string).Assembly.Location, new DecompilerSettings());
6 var name = new FullTypeName(typeof(string).FullName);
7 var code = decompiler.DecompileTypeAsString(name);

       ICSharpCode.Decompiler 的最新版 6.0.1 預覽版1 支持最新框架的程序集反編譯,同時支持了大多數 C# 特性,包括部分C# 8.0 剛出的新特性。

       如今的 .Net Core 真是能夠玩出各類騷操做,太好玩了。固然,在下面的演示代碼中有一個集成在網站項目的演示,下載後運行 IdentityServer 項目並訪問 /OnlineCode/CSharp 和 /DeCompiler/CSharp 就能夠查看效果。C# 腳本大多數演示代碼在 CSharpScriptDemo 項目中。

 

       效果預覽

腳本運行

 

反編譯代碼

 

       轉載請完整保留如下內容,未經受權刪除如下內容進行轉載盜用的,保留追究法律責任的權利!

  本文地址:http://www.javashuo.com/article/p-rsgarloi-dp.html

  完整源代碼:Github

  裏面有各類小東西,這只是其中之一,不嫌棄的話能夠Star一下。

相關文章
相關標籤/搜索