Reflection c#- 反射

  • 5643
  • 0

簡單紀錄 C# 中反射Reflection 的基本使用原則。

個人認知通常在使用工廠模式或者需要動態產生實例的需求情況下會使用反射的機制來達到變更靜態的設定文檔就能動態控制整個系統的設定。

參考網站 : 


 

若需要觀看反射出來的資料內容,在執行時期中使用即時運算視窗看起來會比較舒適。

方案內容 :

Reflection 使用原則 : 

引用類別庫程式碼(ReflectionTargetOb專案): 這個空間下都是等等要反射的對象
using System;

namespace ReflectionTargetOb
{
    public class EntityData1
    {
        [My(1)]
        public int Id { get; set; }
        [My(2)]
        public string Name { get; set; }
        [My(3)]
        public DateTime Birthday { get; set; }
    }

    [System.AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
    public class MyAttribute : Attribute
    {
        readonly int _NamedInt;

        // This is a positional argument
        public MyAttribute(int namedInt)
        {
            this._NamedInt = namedInt;
        }

        public int NamedInt {
            get { return _NamedInt; }
        }
    }

    public class TargetPerson
    {
        private string _name;
        public string Name { get { return _name; } set { _name = value; } }

        private int _age;
        public int Age { get { return _age; } set { _age = value; } }

        public void Show()
        {
            Console.WriteLine("我是TargetPerson類別中的Show方法!");
        }

        public void Say()
        {
            Console.WriteLine("我的名字叫{0},今年{1}歲", this.Name, this.Age);
        }

        public void Add(int i, int j)
        {
            Console.WriteLine(i + j);
        }

        private void _Method1() { }
    }
}
 
Reflection主程式(ReflectionPractice 專案) : 這裡不列出結果,可從 Main 進入點中觀看執行
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ReflectionTargetOb;

namespace ReflectionPractice
{
    /// <summary>
    /// 此為練習 反射取得物件(.dll,.exe...etc)屬性方法或其他資訊
    /// </summary>
    class Program
    {
        // 組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。或換個角度來想,一個Class (類別)裡有什麼東西?
        static void Main(string[] args)
        {
            // 反射有以下三種方式 1.字串 2.類別 3.實例物件
            var t1 = Assembly.Load("ReflectionTargetOb").GetType("ReflectionTargetOb.TargetPerson"); // 1.
            Console.WriteLine(t1.ToString());

            var t2 = typeof(TargetPerson); // 2.
            Console.WriteLine(t2.ToString());
            var t2Instance = Activator.CreateInstance(t2); // 實例化類別(類型)


            var ob1 = new TargetPerson(); // 3.
            var t3 = ob1.GetType();
            var t3Instance = Activator.CreateInstance(t3); // 實例化類別(類型)

            // 由實體.dll,.exe...etc檔案結構獲取相關屬性 Assembly
            Assembly assembly1 = Assembly.
                LoadFrom(@"C:\CSharpStudy\ReflectionTraning\ReflectionPractice\ReflectionTargetOb\bin\Debug\ReflectionTargetOb.dll");
            // Assembly assembly2 = Assembly.ReflectionOnlyLoad 若只要取得相關資訊,可使用此方法增加程式效能
            Console.WriteLine(assembly1.GetType("ReflectionTargetOb.TargetPerson").ToString());
            var assembly1Instance = assembly1.CreateInstance("ReflectionTargetOb.TargetPerson", true);
            /*
                使用 Assembly 類可取得物件或組件內部相關的資訊,有三種方式
                1. Assembly.LoadFrom() - 針對路徑作加載的動作,並取得物件的屬性(有可能會發生例外)
                2. Assembly.Load() - 取得已引入的組件的相關資訊
                3. Assembly.LoadFile() - 針對路徑作加載的動作,並取得物件的屬性但並不會取得該物件自身引入(依賴)的組件
             */

            Module module1 = assembly1.GetModules()[0]; // 將物件的屬性..等資料,轉換成 Module
            Console.WriteLine(module1.GetType("ReflectionTargetOb.TargetPerson").ToString());

            // 執行時期取得物件資訊
            ReflecationObMethod(t2);

            // 執行時期取得物件欄位資訊
            ReflecationObProperty(t2);

            // 執行時期使用指定物件名稱,設定屬性並執行相關方法
            SimpleReflection("ReflectionTargetOb", "TargetPerson");

            // 使用 Dictionary 當作資料,動態取得物件欄位並賦予值
            ReflectionDataIn();

            // 通過System.Reflection.FieldInfo 能查找到類裡面私有或公開的屬性
            ReflectionFieldInfo("ReflectionTargetOb", "TargetPerson");

            // 取得類型中欄位所擁有的特性(Attribute)
            ReflectionCustomAttributs("ReflectionTargetOb", "EntityData1");

            Console.ReadKey();
        }

        // 取得物件執行時期的相關方法, 經測試取得物件所有公開屬性與方法外, 還取得.Net原生配給給Object的公開屬性及方法
        static void ReflecationObMethod(Type type)
        {
            MethodInfo[] methodInfos = type.GetMethods();
            Console.WriteLine();
            Console.WriteLine("使用MethodInfo裝載物件資訊: " + type.Name + " -----");
            foreach (var methodInfo in methodInfos)
                Console.WriteLine(methodInfo);
            Console.WriteLine("依據方法名稱(字串)取得該方法的中繼資料");
            MethodInfo methodShow = type.GetMethod("Show"); // 依據方法名稱取得該方法的中繼資料
            methodShow.Invoke(Activator.CreateInstance(type), null); // 調用方法並執行
            Console.WriteLine("-------------------------------");
        }

        // 執行時期取得物件欄位資訊
        static void ReflecationObProperty(Type type)
        {
            PropertyInfo[] PropertyInfoList = type.GetProperties();
            Console.WriteLine();
            Console.WriteLine("使用PropertyInfo裝載物件資訊: " + type.Name + " -----");
            foreach (var propertyInfo in PropertyInfoList)
                Console.WriteLine(propertyInfo);
            Console.WriteLine("使用 SetValue設定物件屬性, GetProperty 取得物件屬性");
            PropertyInfo typePropertyInfo = type.GetProperty("Name"); // 依據屬性名稱取得屬性的中繼資料
            var ob1 = Activator.CreateInstance(type); // 使用類型實例化
            typePropertyInfo.SetValue(ob1, "設定屬性值"); // 使用欄位資訊設定 ob1 相對應的屬性
            Console.WriteLine("使用GetValue取值: " + typePropertyInfo.GetValue(ob1)); // 使用欄位資訊取得 ob1 相對應的屬性
            Console.WriteLine("-------------------------------");
        }

        // 依據以引入的物件名稱,取得物件指定方法、指定欄位. 並且設定物件相關屬性
        static void SimpleReflection(string classNameSpace, string className)
        {
            Console.WriteLine();
            Console.WriteLine("依據以引入的物件名稱,取得物件指定方法、指定欄位. 並且設定物件相關屬性--");
            Assembly assembly = Assembly.Load(classNameSpace); // 取得引入物件的屬性資訊
            Type type = assembly.GetType(classNameSpace + "." + className); // 取得空間中指定名稱的類別
            var ob1 = Activator.CreateInstance(type); // 建立指定類型的實例
            Console.WriteLine("物件資訊: " + type.FullName);

            // 取得指定方法名稱的方法資訊
            Console.WriteLine("取得指定方法名稱的方法資訊,並且執行");
            MethodInfo methodInfo = type.GetMethod("Show"); // 裝載指定無參的方法
            methodInfo.Invoke(ob1, null); // 調用方法並執行
            methodInfo = type.GetMethod("Add"); // 裝載指定有參的方法
            methodInfo.Invoke(ob1, new object[] { 1, 2, }); // 調用並執行

            // 取得指定欄位名稱的欄位資訊,並用物件指定方法顯示欄位資訊
            Console.WriteLine("取得指定欄位名稱的欄位資訊,並用物件指定方法顯示欄位資訊");
            PropertyInfo propertyInfo = type.GetProperty("Name"); // 裝載指定的欄位資訊
            propertyInfo.SetValue(ob1, "Kevin"); // 依據欄位資訊設定物件中的欄位
            // propertyInfo.GetValue(ob1); // 依據欄位資訊取得物件中的欄位資訊
            propertyInfo = type.GetProperty("Age"); // 裝載指定的欄位資訊
            propertyInfo.SetValue(ob1, 27); // 依據欄位資訊設定物件中的欄位
            methodInfo = type.GetMethod("Say"); // 裝載指定無參的方法
            methodInfo.Invoke(ob1, null);  // 調用並執行
            Console.WriteLine("--------------------------------------------------------");
        }

        // 使用 Dictionary 當作資料,動態取得物件欄位並賦予值
        static void ReflectionDataIn()
        {
            var type = typeof(EntityData1); // 取得類別資料
            var ob1 = Activator.CreateInstance(type); // 依指定類型產生實例
            Console.WriteLine();
            Console.WriteLine("物件資訊: " + type.FullName + " ------------");
            // 創建字典,也可用.xml檔來取代
            Dictionary<string, string> dictionary = new Dictionary<string, string>() {
                { "Id","1" }, {"Name","勇猛戰士"}, {"Birthday","2020-01-13"}
            };
            PropertyInfo[] propertyInfos = type.GetProperties(); // 取得類別欄位資料
            foreach (var property in propertyInfos)
            {
                if (dictionary.Keys.Contains(property.Name)) // 若字典的Key值與欄位名稱相同
                {
                    // 依指定欄位資料設定Ob1的欄位,並且搭配該欄位資料的型態來轉型
                    property.SetValue(ob1, Convert.ChangeType(dictionary[property.Name], property.PropertyType));
                    // 取得指定的欄位資料並列印出來
                    Console.WriteLine(property.GetValue(ob1));
                }
            }
            Console.WriteLine("------------------------------------------------");
        }

        // 通過System.Reflection.FieldInfo 能查找到類裡面私有或公開的屬性
        static void ReflectionFieldInfo(string classNameSpace, string className)
        {
            Console.WriteLine();
            Console.WriteLine("通過System.Reflection.FieldInfo 能查找到類裡面私有或公開的屬性---");
            Assembly assembly = Assembly.Load(classNameSpace); // 取得引入物件的屬性資訊
            Type type = assembly.GetType(classNameSpace + "." + className); // 取得空間中指定名稱的類別
            Console.WriteLine("物件資訊: " + type.FullName);
            // BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public |
            FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (var field in fieldInfos)
                Console.WriteLine(field.Name);
            Console.WriteLine("------------------------------------------------");
        }

        // 取得類型中欄位所擁有的特性(Attribute)
        static void ReflectionCustomAttributs(string classNameSpace, string className)
        {
            Console.WriteLine();
            Console.WriteLine("取得類型中欄位所擁有的特性");
            Assembly assembly = Assembly.Load(classNameSpace); // 取得引入物件的屬性資訊
            Type type = assembly.GetType(classNameSpace + "." + className); // 取得空間中指定名稱的類別
            Console.WriteLine("物件資訊: " + type.FullName);
            //var a = type.GetProperty("Name").GetCustomAttributes();
            //var customAttributeDatas = (MyAttribute[])type.GetCustomAttributes(typeof(MyAttribute), false);
            type.GetProperties().ToList().ForEach(x => { // 取得所有欄位後,針對指定特性轉型並印出
                var attribute = x.GetCustomAttribute(typeof(MyAttribute), false) as MyAttribute;
                Console.WriteLine(attribute);
                Console.WriteLine(attribute.NamedInt);
            });
        }
    }
}

 

簡單示範工廠模式搭配反射(ReflectionSimpleFactory專案) : 

config文檔 : 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key ="DAL" value ="ReflectionSimpleFactory.Access" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>
</configuration>
主程式 : 
using System;
using System.Configuration;
using System.Reflection;

namespace ReflectionSimpleFactory
{
    interface IDAL
    {
        void Insert();
    }

    class SqlServer : IDAL
    {
        public void Insert()
        {
            Console.WriteLine("SqlServer增加一條記錄!");
        }
    }

    class Access : IDAL
    {
        public void Insert()
        {
            Console.WriteLine("Access增加一條記錄!");
        }
    }

    // 簡單釋例工廠模式應用反射
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("ReflectionSimpleFactory");
            string str = ConfigurationManager.AppSettings["DAL"]; // 取得在 config 中設定的值
            Type type = assembly.GetType(str);
            IDAL DAL = Activator.CreateInstance(type) as IDAL;
            DAL.Insert();

            Console.ReadKey();
        }
    }
}

 

 


多多指教!! 歡迎交流!!

你不知道自己不知道,那你會以為你知道