上方廣告

Lady Kukki 手作糖霜餅乾

2012年7月4日 星期三

解決Reflection找不到繼承其它類別之類別

前幾天說明了如何使用dynamic來取代Reflection的功能,寫完文章後自己又玩了一下,卻無意間發現另外的問題,如果要操作的類別與繼承的類別並不是在相同的dll的話會取不到類別,dynamic與 Reflection的結果都一樣, 在發生問題的當下以為是namespance寫錯的關係,不過反覆調整了幾次發現都不是,於是就陷入了混亂的思緒,這時候就證明了當你以呈現混亂的思緒還是離開當下的環境比較好,回到家洗完澡後就想到一個問題,會不會是子類別讀不到父親類別的參考(雖然我編譯時期有參考)但是編譯成dll檔後它們並不互相認識,於是我就很天才的把兩個dll放在同一目錄底下,然後很開心的一試,還是掛掉(理所當然的吧),再把父親類別跟子類別放在同一個專案下編譯就正常,當下覺得很奇怪。
後來再仔細思考後想到,以前有寫的動態產生程式碼的時候也有類似的case,在動態編譯程式碼的時候要記得把參考加入到Assembly當中讓執行的當下程式可以找的到參考,於是就上了MSDN找答案,找到一個很像可以使用的屬性 ReflectionOnlyLoadFrom,經過多次的革命後終於成功了。

以下我一一進行說明我測試成功的過程吧。 以下是我等等要操作的檔案
1. 父親類別進行編譯後檔案【Lawrence.BaseClass.dll】。
2. 子類別進行編譯後檔案【Lawrence.Class.dll】。
3. 操作的主程式【Lawrence.Reflection.exe】。 

A. 首先先建立父親類別Lawrence.BaseClass.Parent
namespace Lawrence.BaseClass
{
    public partial class Parent
    {
        public string Name;
        public virtual string getName()
        {
            return string.Format("Parent name is {0}", Name); 
        }
    }
}

B. 子類別Lawrence.Class.Son
namespace Lawrence.Class
{
    public class Son : Lawrence.BaseClass.Parent
    {
        public override string getName()
        {
            return string.Format("Parent name is {0}", Name);
        }
    }
}

C. 接下來就是我的測試過程,首先用一開始我們常用的LoadFrom的方式載入,就如同我所說明的一樣找不到。
//使用 Assembly.LoadFrom載入的方式會找不到相關的類別
Assembly assembly = Assembly.LoadFrom(@"D:\Reflection\Lawrence.Class\bin\Debug\Lawrence.Class.dll");
Type type = assembly.GetType("Lawrence.Class.Son");

if (type == null)
{
    dynamic instance = Activator.CreateInstance(type);
    Console.WriteLine("type is null");
}


D. 改用再來試試ReflectionOnlyLoadFrom,這時候會發現出現另外一個奇怪的錯誤訊息【因為沒有預先載入相依性,無法解析相依性至組件 'Lawrence.BaseClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'。在使用 ReflectionOnly API 時,必須預先載入或透過 ReflectionOnlyAssemblyResolve 事件視需要載入相依組件】。
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(@"D:\Reflection\Lawrence.Class\bin\Debug\Lawrence.Class.dll");
Type type = assembly.GetType("Lawrence.Class.Son");

if (type == null)
{
    dynamic instance = Activator.CreateInstance(type);
    Console.WriteLine("type is null");
}


E. 看到上面的錯誤訊息才讓我想到要想辦法讓父親類別的dll載入執行才有辦法正常運作Assembly有個事件是在處理當反映之內容中的組件解析失敗時的處理動作,AppDomain.ReflectionOnlyAssemblyResolve 事件,該事件在發生解析失敗時要處理的動作。
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
Assembly assembly1 = Assembly.ReflectionOnlyLoadFrom(@"D:\Reflection\Lawrence.BaseClass\bin\Debug\Lawrence.BaseClass.dll");
Assembly assembly3 = Assembly.LoadFrom(@"D:\Reflection\Lawrence.Class\bin\Debug\Lawrence.Class.dll");

Type type = assembly3.GetType("Lawrence.Class.Son");

if (type != null)
{
    dynamic instance = Activator.CreateInstance(type);
    instance.Name = "Lawrence";
    Console.WriteLine(instance.getName());
}

static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
    return System.Reflection.Assembly.LoadFrom(args.Name);
}


F. 如此一來就完成了,但是聰明的你不知道有沒有發現在解析組件失敗的時候我的處理動作還是使用LoadFrom來載入dll檔,那麼一開始就直接載入該dll檔就好了不是嗎? 的確直接載入要用的父親類別就可以了,所以該範例我只是單存的想要測試該用法而已,而且也藉由這次的經驗才又發現ReflectionOnlyLoadFrom只能單純的載入,並不能做任何操作,一但透過ReflectionOnlyLoadFrom載入的類別要進行操作就會發現出現【ReflectionOnly內容中的要求作業無效】。

本文範例 :
Reflection.zip