為了將問題描述清楚,我們先來看一個例子。在這個例子中,WinForm上有一個按鈕,當用戶點擊這個按鈕後,就會裝載一個已經存在的Assembly,並且在界面的Label控件上顯示出這個Assembly的FullName。對Reflection稍微熟悉一點的朋友都知道,這是非常簡單的事情,只需要用Assembly.LoadFile方法獲得Assembly,然後用FullName屬性來顯示即可,比如下面的代碼:
private void button1_Click(object sender, EventArgs e)
{
Assembly assembly = Assembly.LoadFile(@"C:\testlib.dll");
label1.Text = assembly.FullName;
}
當然,程序執行正常,您不會發現任何編譯時或運行時的錯誤。然而,當你在沒有退出此程序之前,再去編譯被調用的testlib.dll,你會發現,Visual Studio無法完成編譯,提示說該文件正在被其它的進程所使用。
事實上,我們的程序與這個testlib.dll並沒有太大的關聯,我們的程序只不過就是顯示一下testlib.dll的基本信息。如果testlib.dll是一個共享的庫,那麼資源獨占問題會影響到其它程序的正常工作。
Assembly沒有Unload的功能,但可以使用AppDomain來解決這個問題。基本思路是,創建一個新的AppDomain,在這個新建的AppDomain中裝載assembly,調用其中的方法,然後將獲得的結果返回。在完成所有操作以後,調用AppDomain.Unload方法卸載這個新建的AppDomain,這樣也同時卸載了assembly。注意:你無法將裝載的assembly直接返回到當前應用程序域(AppDomain)。
首先,創建一個RemoteLoader,這個RemoteLoader用於在新建的AppDomain中裝載assembly,並向外公布一個屬性,以便外界能夠獲得assembly的FullName。RemoteLoader需要繼承於MarshalByRefObject。代碼如下:
public class RemoteLoader : MarshalByRefObject
{
private Assembly assembly;
public void LoadAssembly(string fullName)
{
assembly = Assembly.LoadFrom(fullName);
}
public string FullName
{
get { return assembly.FullName; }
}
}
其次,創建一個LocalLoader。LocalLoader的功能是創建新的AppDomain,然後在這個新的AppDomain中調用RemoteLoader,以便通過RemoteLoader來創建assembly並獲得assembly的相關信息。此時被調用的assembly自然被裝載於新的AppDomain中。最後,LocalLoader還需要提供一個新的方法,就是AppDomain的卸載。代碼如下:
public class LocalLoader
{
private AppDomain appDomain;
private RemoteLoader remoteLoader;
public LocalLoader()
{
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = "Test";
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
setup.CachePath = setup.ApplicationBase;
setup.ShadowCopyFiles = "true";
setup.ShadowCopyDirectories = setup.ApplicationBase;
appDomain = AppDomain.CreateDomain("TestDomain", null, setup);
string name = Assembly.GetExecutingAssembly().GetName().FullName;
remoteLoader = (RemoteLoader)appDomain.CreateInstanceAndUnwrap(
name,
typeof(RemoteLoader).FullName);
}
public void LoadAssembly(string fullName)
{
remoteLoader.LoadAssembly(fullName);
}
public void Unload()
{
AppDomain.Unload(appDomain);
appDomain = null;
}
public string FullName
{
get
{
return remoteLoader.FullName;
}
}
}
最後,修改我們WinForm上的Button Click事件處理過程,改為如下的形式:
private void button1_Click(object sender, EventArgs e)
{
LocalLoader ll = new LocalLoader();
ll.LoadAssembly(@"C:\testlib.dll");
label1.Text = ll.FullName;
ll.Unload();
}
在完成上述的修改後,我們的程序也同樣能夠正確地顯示assembly的FullName,而且,在顯示完assembly信息後,程序會主動卸載新建的AppDomain,以防止testlib.dll的資源獨占,影響其它程序的運行。
查看本欄目