C#反射


在前面簡單學習了Linq To Object的常用標准查詢運算符,在我們項目開發中,運用Linq表達式方便了我們編程,而接下來要講的另一個內容——反射,也能很好地幫助我們處理某些特殊的情況。

 

一、反射的概念: 

反射提供了封裝程序集、模塊和類型的對象(Type類型)。可以使用反射動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型並調用其方法或訪問其字段和屬性。如果代碼中使用了屬性,可以利用反射對它們進行訪問。

一般使用:工廠類,通過反射創建類的實例,實現層與層之間的解耦:  數據層→數據會話層→業務邏輯層。 其中,數據會話層通過反射創建數據層的實例,業務邏輯層調用。

 

二、反射Type中的四個函數:

復制代碼
            Person person = new Person();
            Student student = new Student();
            //判斷指定的兩個成員是否存在繼承關系(判斷一個類是否可用賦值給另一個類)    --后者繼承於前者
            bool b1 = typeof(Person).IsAssignableFrom(typeof(Student));    //student繼承了person
            bool b3 = person.GetType().IsAssignableFrom(student.GetType());//另一種寫法
            bool b2 = typeof(Itest).IsAssignableFrom(typeof(Teacher));

            //判斷是否是指定類的實例
            bool b4 = typeof(Person).IsInstanceOfType(student);     //結果為true   student繼承了person
            bool b5 = person.GetType().IsInstanceOfType(student);//另一種寫法   GetType當前對象的實例

            //判斷是否是某個類的子類,非接口
            bool b6 = typeof(Student).IsSubclassOf(typeof(Person));

            //判斷是否是抽象
            if (typeof(Drive).IsAbstract)
            {
                Console.WriteLine("是抽象的");
            }
            else
            {
                Console.WriteLine("不是抽象的");
            }
            Console.ReadKey();
復制代碼

要使用到的類和接口:

    class Person{}
    class Student:Person{}
    class Teacher:Itest{}
    interface Itest{}
    public abstract class Drive{}

 

三、反射中常用類的使用:

需求:通過反射獲得Common程序集中的成員,並使用成員。

Comman程序集:

復制代碼
    public class FileCommon
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public char Gender { get; set; }
        public FileCommon(string name)
        {
            this.Name = name;
        }
        public void WriteData(string path,string content)
        {
            File.WriteAllText(path, content, Encoding.Default);
        }
    }
復制代碼

 

1、把Common.dll放到該應用程序的bin/Debug目錄下

string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"Common.dll");
Assembly ass = Assembly.LoadFile(path);  //需要絕對路徑  LoadFile加載路徑程序集內容        -->也不一定要在debug目錄下,自己構建絕對路徑也可
Type[] types = ass.GetTypes();             //獲得加載到的程序集中的所有數據類型,包括公開和不公開的    
//Type[] types = ass.GetExportedTypes(); //獲取加載到的公開程序集中的數據

 

2、可以遍歷程序集中的類型,獲取類型的命名空間和類型名稱

foreach (Type item in types)
{ 
    Console.WriteLine(item.Name);             //類型名稱  --也就是common下類或者接口的名稱
    Console.WriteLine(item.Namespace);        //命名空間
}

 

3、獲取指定程序集中的數據類型

            //獲得指定程序集中數據類型,包括公開的和不公開的
            Type type = ass.GetType("Common.FileCommon");
            Console.WriteLine(type.Name);
            Console.WriteLine(type.Namespace);

 

4、創建type對象

      a:創建沒有構造函數的對象

object o = ass.CreateInstance("Common.FileCommon");     //FileCommom為Common命名空間的一個類 → 命名空間.類名 

    b:反射出來的類型有構造函數

 object o2 = Activator.CreateInstance(type, "參數");

注意: 當反射出來的類型如果有構造函數,用上面代碼中 ass.CreateInstance則會出現錯誤,如果有構造函數,那么該如何知道構造函數的參數?

ConstructorInfo [] info = type.GetConstructors();    //查詢所有的構造函數,可以看到構造函數需要傳遞參數的參數類型

 

5、獲得數據類型中所有的屬性

            //獲得數據類型中所有的屬性
            PropertyInfo[] pinfo = type.GetProperties();   //獲取屬性  然后可以遍歷
            foreach (PropertyInfo item in pinfo)
            {
                Console.WriteLine(item.Name);
            }

 

6、獲得數據類型中所有的函數

            //獲得數據類型中所有的方法函數
            MethodInfo[] minfo = type.GetMethods();   //獲取所有的函數
            foreach (MethodInfo item in minfo)
            {
                Console.WriteLine(item.Name);
            }

 

7、目的:調用函數

            //調用WriteData函數
            MethodInfo method = type.GetMethod("WriteData");    //該類中的writeData方法
            object o3 = method.Invoke(o2, new object[] { "2.txt", "通過反射調用噠" });
            Console.WriteLine("調用成功");
            Console.ReadKey();

 

四、小結: 

當獲得一個 .dll程序集的時候,需要先獲取所有的類型,也就是這個程序集中的類,然后根據所需要類的名稱,去創建指定名稱的對象,如:Type type = ass.GetType("Common.FileCommon");  ,然后利用type獲取構造函數、所有函數、屬性(還有一些可以 type.方法名來獲取相應的需求),再選擇創建type對象的方法(構造或無構造),然后根據獲得的函數來調用,傳入相應的參數。

 

五、具體應用例子:

使用反射制作關機插件

1、先定義關機接口和具體關機的插件方法:

    public interface IPlugin
    {
        string Name { get; }
        //負責關閉計算機
        void GuanJi(int t);
    }
復制代碼
    public class ShutDownClass : IPlugin
    {
        public void GuanJi(int t)
        {
            //關機
            ShutDown(t.ToString());
            //返回一個K類型的默認值
        }

        public void ShutDown(string second)
        {
            Process process = new Process();
            process.StartInfo.FileName = "cmd.exe";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;
            process.Start();
            process.StandardInput.WriteLine("shutdown -s -f -t " + second);
            process.StandardInput.WriteLine("exit");

            process.Close();
            process.Dispose();
        }
        public string Name
        {
            get { return "關機"; }
        }
    }
復制代碼

2、窗體調用:

復制代碼
        private void Form1_Load(object sender, EventArgs e)
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plug");   //bin/debug/plug存放.dll文件

            string[] files = Directory.GetFiles(path);
            foreach (string item in files)
            {
                Assembly ass = Assembly.LoadFile(item);
                Type[] types = ass.GetExportedTypes();
                for (int i = 0; i < types.Length; i++)
                {
                    //判斷是否為接口中的類,並不是抽象類
                    if (typeof(IPlugin).IsAssignableFrom(types[i]) && !types[i].IsAbstract)
                    {
                        //創建對象
                        o = Activator.CreateInstance(types[i]);
                        //獲得指定對象
                        PropertyInfo pif = types[i].GetProperty("Name");
                        object o2 = pif.GetValue(o);
                        //添加到菜單欄
                        ToolStripItem tsi = PluginToolStripMenuItem.DropDownItems.Add(o2.ToString());
                        //把數據對象存儲到要使用的對象
                        tsi.Tag = types[i];
                        //給添加的tsi注冊單擊事件
                        tsi.Click += (s, e2) =>
                        {
                            Type t = tsi.Tag as Type;
                            MethodInfo mi = t.GetMethod("GuanJi");
                            mi.Invoke(o, new object[] { 3000 });
                        };
                    }
                }
            }
        }
復制代碼

六、總結: 

很多.dll文件可以通過反射的方式來獲取相應的類,類的方法屬性的使用,一些應用程序也可以通過反編譯軟件來獲取一些方法屬性等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM