一、前世今生
.NET誕生以來,程式集的動態載入和解除安裝都是一個Hack的技術,之前的NetFx都是使用AppDomain的方式去載入程式集,然而AppDomain並沒有提供直接解除安裝一個程式集的API,而是要解除安裝整個AppDomain才能解除安裝包含在其中的所有程式集。然而解除安裝整個CurrentAppDomain會使程式不能工作。可能有人另闢西經,建立別一個AppDomain來載入/解除安裝程式集,但是由於程式集之間是不能跨域訪問的,也導致只能通過Remote Proxy的方式去訪問,這樣在型別建立和使用上帶來了一定的難度也是型別的繼承變得相當複雜。
.NET Core中一直沒有AppDomain的支援。但是在.NET Core 3.0中,我最期待的一個特性就是對可收集程式集的支援(Collectible AssemblyLoadContext)。 眾所周知.NET Core中一直使用AssemblyLoadContext的API,來進行程式集的動態載入,但是並沒有提供Unload的方法,此次升級更新了這方面的能力。
二、AssemblyLoadContext
其實這次AssemblyLoadContext的設計,我認為更像是Java中ClassLoader的翻版,可以說非常類似。在使用過程中自定義AssemblyLoadContext可以內部管理其中的程式集,並對整體Context進行Unload。使用AssemblyLoadContext也可以避免程式集名稱和版本的衝突。
三、Getting Started
.NET Core 3.0還沒有正式版,所有要使用預覽版的SDK完成以下例項。我使用的是.NET Core SDK 3.0.100-preview-009812
dotnet new globaljson --sdk-version 3.0.100-preview-009812
AssemblyLoadContext是一個抽象類的,我們需要子類化。下面顯示的是我們建立自定義AssemblyLoadContext的方法,實現一個可回收的Context需要在構造器中指定isCollectible: true :
public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
public CollectibleAssemblyLoadContext() : base(isCollectible: true)
{ }
protected override Assembly Load(AssemblyName assemblyName)
{
return null;
}
}
使用netstandard2.0建立一個library
using System;
namespace SampleLibrary
{
public class SayHello
{
public void Hello(int iteration)
{
Console.WriteLine($"Hello {iteration}!");
}
}
}
測試Load/Unload
var context = new CollectibleAssemblyLoadContext();
var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(),"SampleLibrary.dll");
using (var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
{
var assembly = context.LoadFromStream(fs);
var type = assembly.GetType("SampleLibrary.SayHello");
var greetMethod = type.GetMethod("Hello");
var instance = Activator.CreateInstance(type);
greetMethod.Invoke(instance, new object[] { i });
}
context.Unload();
GC.Collect();
GC.WaitForPendingFinalizers();
當執行GC收回後,載入的程式集會被完全的回收。
四、最後
GitHub:https://github.com/maxzhang1985/YOYOFx 如果覺還可以請Star下, 歡迎一起交流。
.NET Core 開源學習群:214741894