深入探討 .NET Core 之 Type Forwarding
.NET Core 能跨平台看似很神奇,事實上底層所使用的技術為 Type Forwarding,說穿了就是 Proxy Pattern 的應用。
Version
macOS High Sierra 10.13.3
.NET Core SDK 2.1.101
JetBrains Rider 2017.3.1
Visual Studio 2017 15.6.2
Definition
Client 在不改變 assembly reference 狀態下,就可使用其他 assembly 所提供的 class。
.NET App 會直接與 Assembly A
耦合,僅能使用 Assembly A
的 MyClass
。
.NET App 依然只與 Assembly A
耦合,但卻能夠透過 Type Forwarding 使用 Assembly B
的 MyClass
。
實戰 Type Forwarding
建立 ClassLibrary1
Class1.cs
1 | namespace ClassLibrary1 |
在 ClassLibrary1
assembly 建立 Class1.Sum()
。
建立 ConsoleApp
Program.cs
1 | using System; |
在 ConsoleApp
使用 ClassLibrary1
的 Class1.Sum()
。
- 目前只有
ClassLibrary1
與ConsoleApp
- 執行結果為
2
ConsoleApp
的 project reference 只有ClassLibrary1
建立 ClassLibrary2
Class1.cs
1 | namespace ClassLibrary1 |
將 ClassLibrary1
的 Class1.cs
全部程式碼搬到 ClassLibrary2
。
ClassLibrary1 使用 Type Forwarding
Class1.cs
1 | using System.Runtime.CompilerServices; |
因為 ClassLibrary1
的 Class1.cs
程式碼已經搬到 ClassLibrary2
,將原本 ClassLibrary1
的 Class1.cs
加上 Type Forwarding。
當 ConsoleApp
使用 ClassLibrary1
assembly 的 ClassLibrary1.Class1
時,會自動 Type Forwarding 到 ClassLibrary2
assembly 的 ClassLibrary1.Class1
。
Rebuild 整個 solution 的 3 個 project 並執行。
- 目前有
ClassLibrary1
、ClassLibrary2
與ConsoleApp
- 執行結果依然為
2
- 原本
ConsoleApp
的 project reference 只有ClassLibrary1
,因為 Type Forwarding,所以現在也有了ClassLibrary2
我們並沒有對
ConsoleApp
將ClassLibrary2
加入 project reference,而是因為 Type Forwarding
觀察 netstandard Assembly
Metapackage 與 .NET Standard 談了這麼多,是否感覺很抽象呢 ? 讓我們以反組譯 netstandard
Assembly 來理解其中的黑魔法。
使用 Visual Studio 2017 開啟 solution。
View -> Object Browser 開啟 Object Browser,可以讓我們看到整個 solution 所用到的 assembly。
- 展開
ClassLibrary1
,看不到任何 namespace 與 class,因為我們已經在ClassLibrary1
使用 Type Forwarding。 - 展開
ClassLibrary2
,我們看到了ClassLibrary1
namespace 與Class1
class。
若 assembly 使用了 Type Forwarding,使用 Object Browser 將看不到該 assembly 任何實作的 namespace 與 class
- 點擊
mscorlib
與netstandard
兩個 assembly ,亦發現沒有任何實作的 namespace 與 class,其中mscorlib
就是Microsoft.NETCore.App
,而netstandard
就是NETStandard.Library
- 顯示
netstandard
assembly 的實際路徑
由於
mscorlib
與netstandard
都沒有任何實作,根據ClassLibrary1
的經驗推測,很可能也是用了 Type Forwarding
使用 ILSpy 反組譯 netstandard
assembly,我們可以發現真的沒有任何實作,但大量使用了 Type Forwarding。
在傳統 .NET Framework 世界裡,我們是直接參考 .NET Framework 內某個 assembly 的 class,也因此與 Windows 平台的 assembly 的 class 耦合。
但在 .NET Core 世界裡,我們改參考 .NET Standard,再由 .NET Standard 透過 Type Forwarding 方式參考實際 host 的 assembly 的 class,若 host 是 macOS,則參考 macOS 上的 assembly 的 class,若 host 是 Linux,則參考 Linux 上的 assembly 的 class。
因為 client 只與有 Type Forwarding 的 .NET Standard 耦合,而沒與特定平台的 class 耦合,所以 .NET Core 能達到跨平台,在不同 host os 下,不用編譯就可以自動找到正確的 class
Proxy Pattern
Proxy Pattern
Client 為了避免與實際物件耦合,改透過相同 interface 的
白手套
proxy 耦合,再由 proxy 負責與實際物件溝通
Client
實際上要使用 RealSubject
,但為了將 RealSubject
與 Client
解耦合,Client
只與相同 interface 的 Proxy
耦合,再由白手套 Proxy
使用 RealSubject
。
.NET Standard
Client
實際上要使用各平台的 .NET Core SDK
,但為了將各平台的 .NET Core SDK
與 Client
解耦合,Client
只與一樣遵守 .NET Standard 的 netstandard
耦合,再由白手套 netstandard
透過 Type Forwarding 使用各平台的 .NET Core SDK
。
Conclusion
- Metapackage 與 .NET Standard 不是什麼黑魔法,只是一個使用 Type Forwarding 的 DLL
- 我們只要跟 .NET Standard 耦合即可,它會使用 Type Forwarding 幫我們使用各平台實際的 class
Sample Code
完整的範例可以在我的 GitHub 上找到
Reference
Microsoft Docs, Type Fowarding in the Common Language Runtime