Reflection是在執行期間才解析物件類別資訊的技術,在不少場合,要處理的物件類別在編譯時期是無法預知的,或是希望能保留彈性,以便接受包容各種物件。當傳進來的物件參數類別是object,卻又想一探它的底細,就是System.Reflection命名空間神奇工具組上場的時候。

印象中Reflection與編譯期確定型別相比,效率頗差,每次要動用時總有些畏首畏尾的。但我後來發現MS在BCL內,自己倒用Reflection用得很盡興,加上新一代的CPU愈來愈快,效能差異的顯著性正大幅降低。慢慢地我才開始比較大膽應用它解決問題,執行期才動態識別物件的做法,在某些場合大幅簡化程式的複雜度,有時甚至是形成10行Code對100行Code的差距。

每次要寫Reflection都要重新查文件,索性留文一篇給自己也給大家參考。

以下的例子利用Reflection的技術剖析傳入的未知類別object物件,一一巡弋自訂類別SecrectObject的Field、Property,連宣告private的也不放過,呵呵。最後再示範了如何呼叫Method。

using System;
using System.Reflection;
using System.Diagnostics;
 
public class SecretObject
{
    public string PublicProperty { get; set; }
    private string PrivateProperty { get { return "Secrect"; } }
    public string PublicField = "Public";
    private string PrivateField = "Private";
    public static string StaticField = "Static";
    public void SayHello(string name)
    {
        Console.WriteLine("Hello, " + name);
    }
}
 
public class CSharpLab
{
    public static void Test() 
    {
        SecretObject so = new SecretObject();
        so.PublicProperty = "Open";
        Analyze(so);
    }
 
    public static void Analyze(object obj)
    {
        Type t = obj.GetType();
        Console.WriteLine("Type Name={0}", t.Name);
        foreach (FieldInfo f in t.GetFields(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance | BindingFlags.Static))
            Console.WriteLine(
                "Field [{0}] = {1}", f.Name, f.GetValue(obj));
        foreach (PropertyInfo p in t.GetProperties(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance | BindingFlags.Static))
            Console.WriteLine("Property [{0}] = {1}",
                                p.Name, p.GetValue(obj, null));
        foreach (MethodInfo m in t.GetMethods(
            BindingFlags.Public | BindingFlags.Instance ))
        {
            if (m.Name == "SayHello")
            {
                Console.WriteLine("Method [SayHello] Found!");
                foreach (ParameterInfo a in m.GetParameters())
                    Console.WriteLine(" - Parameter : " + a.Name);
                m.Invoke(obj, new object[] { "Jeffrey" });
            }
        }
    }
}

[程式碼可使用MiniCSharpLab直接執行]


Comments

# by chiyuan

大大最近在閱讀Reflection 找到你的文章 我也試著把程式碼貼到 Mini C# Lab Ver 1.3 卻不能work error message: Compilation Errors: Line: 7 Column 36 - 'SecretObject.PublicProperty.get' 不是標記成 abstract 或 extern,因此必須宣告主體 Line: 7 Column 41 - 'SecretObject.PublicProperty.set' 不是標記成 abstract 或 extern,因此必須宣告主體 請問那邊有漏掉? 謝謝

# by chiyuan

大大 請問上述code是要選 .net 3.5 ? 我選3.5 執行無誤~ 3q3q

# by Jeffrey

to chiyuan, 它有用到C# 3.0+才有的自動實作屬性(http://blog.darkthread.net/blogs/darkthreadtw/archive/2008/06/06/c-3-auto-imp-prop.aspx) ,所以要選.NET 3.5囉。

# by vance

大大您好 最近在研究有關Reflection的議題, 不好意思把您很久以前的文章挖出來(我太晚學寫程式了...)。 有個問題想請教您,在使用assembly.CreateInstance動態建立物件時, 若該物件本身有邏輯錯誤,出現內部Exception時,應該要用甚麼方式避免主程式Crash? 由於動態建立的物件並非自己開發,而對方程式若沒有做好Exception管理, 就會造成我們主程式在建立物件時一同出現Error, 我有試過主程式加上TRY CATCH的處理,但仍然無法避免, 不知您有處理過類似的狀況嗎? 萬分感謝~~

Post a comment