由于在Java中有一個可以自己替換的ClassLoader,是以在Eclipse中很容易管理成千上萬的插件,可以在同一個應用域中自由地裝載和解除安裝。在運作時為某個Class重新定義一個ClassLoader并且重新執行個體化,原來加載的東西就交給GC了。這個機制還有一個我一直夢想的一個價值就是在Load一個Class到應用域的時候運作某些方法,例如讀出配置檔案。如果配置檔案中涉及到一些額外的Class需要更替就不必重新啟動應用程式了。這對于需要類似熱部署的場合非常有用。
在.net中沒有這麼幸運。Assembly是決不能被解除安裝的。有一個解決方案是通過解除安裝AppDomain的方式來解除安裝一系列的Assembly。經過一小番試驗,跨域的調用是可以實作的,不過需要遵循一點點小規則。
一、隻有自MashalByRefObject派生的類型可以跨應用域通路。因為Calling的域使用的中Executing域中的執行個體通過Unwrap出來的代理類,是以其實并不是真正的“跨應用域”,而是類似Remoting的方式。
二、所有傳遞的資料類型必須是加上Serializable Attribute的類型。
我的應用場景是通過選項在運作時替換一個已更新的Assembly依賴。這個依賴顯然不能寫到主應用程式所依賴的Assembly清單中,而是通過從配置文檔讀出其相關的Assembly并從指定位置加載。
下面的例子是讀出一個Assembly中的所有類型并将其繼承關系顯示到一個TreeView中。
這是個用來傳遞類型資訊的資料類型:
/**//// <summary>
/// 可在應用域間傳遞的詳細類型資訊
/// </summary>
[Serializable]
public class TypeInfo
{
public TypeInfo(Type type)
_Name = type.Name;
_FullName = type.FullName;
_BaseName = type.BaseType.FullName;
}
private string _Name, _FullName, _BaseName;
public string Name
get
return _Name;
}
public string FullName
return _FullName;
public string BaseName
return _BaseName;
}
這是個裝載器:
/// 跨域工作的程式集裝載器
public class AssemblyLoader : MarshalByRefObject
public TypeInfo [] GetAssemblyTypeInfos(string path)
Assembly assembly = Assembly.LoadFile(path);
Type [] types = assembly.GetExportedTypes();
TypeInfo [] typeInfos = new TypeInfo[types.Length];
int i = 0;
foreach (Type t in types)
typeInfos[i ++] = new TypeInfo(t);
return typeInfos;
剩下的就是用戶端代碼了,拿到這個清單生成TreeView就沒什麼困難了:
private TypeInfo [] GetTypeInfos(string path)
AppDomain appDomain = AppDomain.CreateDomain("TemporaryAssembly");
Type t = typeof(AssemblyLoader);
AssemblyLoader loader = (AssemblyLoader) appDomain.CreateInstance(t.Assembly.FullName, t.FullName).Unwrap();
TypeInfo [] typeInfos = loader.GetAssemblyTypeInfos(path);
AppDomain.Unload(appDomain);
return typeInfos;