6.C#知識點:反射

来源:http://www.cnblogs.com/DingKing/archive/2017/12/24/8097377.html
-Advertisement-
Play Games

1.反射是什麼? 反射提供描述組件,模塊和類型的對象(類型為Type)。您可以使用反射來動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型,並調用其方法或訪問其欄位和屬性。如果您在代碼中使用屬性,反射使您可以訪問它們。有關更多信息,請參閱屬性。 來自微軟官方。 微軟的解釋我覺得還可以。用 ...


1.反射是什麼?

  反射提供描述組件,模塊和類型的對象(類型為Type)。您可以使用反射來動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型,並調用其方法或訪問其欄位和屬性。如果您在代碼中使用屬性,反射使您可以訪問它們。有關更多信息,請參閱屬性。-----來自微軟官方。

  微軟的解釋我覺得還可以。用大白話講就是我們可以以通過反射讓我們知道位置類型的信息。類似顯示生活中的B超啊。醫生用B超看到孕婦肚子里的內部情況,因為醫生無法從內部查看。反射也是一樣,對於位置類型。或者引用過來的dll。我們是不知道內部情況的。但是可以通過反射。蝙蝠的超聲波也是。通過聲波反射回來,得知前方是否有障礙。這就是反射的功能。如果要問反射內部是如何實現的。不好意思。目前我也不知道。哈哈哈哈。

  簡單的來說,我們的程式是有dll的組成的,dll裡面有許許多多的類組成。類裡面又有欄位,屬性和方法。反射的作用就是給個dll就能知道有哪些類,通過類又能知道有哪些成員。那麼.net裡面的反射是怎麼做到呢?那下麵就要介紹幾個種類的反射類了。

    (1)使用Assembly定義和載入程式集,載入在程式集清單中列出模塊,以及從此程式集中查找類型並創建該類型的實例。 
    (2)使用Module瞭解包含模塊的程式集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。 
    (3)使用ConstructorInfo瞭解構造函數的名稱、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。 
    (4)使用MethodInfo瞭解方法的名稱、返回類型、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。
    (5)使用FiedInfo瞭解欄位的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,並獲取或設置欄位值。
    (6)使用EventInfo瞭解事件的名稱、事件處理程式數據類型、自定義屬性、聲明類型和反射類型等,添加或移除事件處理程式。 
    (7)使用PropertyInfo瞭解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,獲取或設置屬性值。 
    (8)使用ParameterInfo瞭解參數的名稱、數據類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。這段話是從大牛的博客拷貝------->傳送門

  

下麵我依次重點的介紹幾個詳細的類。

首先是Assembly。這個存在於System.Reflection命名空間下麵。我主要講它的3個載入程式集的的方法和區別。Load,LoadForm,LoadFile。

講了這門多文字,先從代碼看看語法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            //載入程式集
            Assembly assembly = Assembly.Load("TestDLL");

            //輸出程式集的強名稱
            Console.WriteLine(assembly.FullName);
            Console.ReadKey();
        }
    }
}

Load方法就是通過程式集的的名稱載入程式,但是需要要載入的程式集在當前程式集的bin目錄下才能找得到。

LoadForm

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            #region Load方法
                    //載入程式集
                   // Assembly assembly = Assembly.Load("TestDLL");

                    //輸出程式集的強名稱
                    //Console.WriteLine(assembly.FullName);
                    //Console.ReadKey();
            #endregion

            Assembly assmbly1 = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");
            Console.WriteLine(assmbly1.FullName);
            Console.ReadKey();
        }
    }
}

LoadForm是通過路徑進行創建。返回載入的程式集。

來看最後一個loadFile

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            #region Load方法
            //載入程式集
            // Assembly assembly = Assembly.Load("TestDLL");

            //輸出程式集的強名稱
            //Console.WriteLine(assembly.FullName);
            //Console.ReadKey();
            #endregion

            #region LoadFrom方法
            //Assembly assmbly1 = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");
            //Console.WriteLine(assmbly1.FullName);
            ///Console.ReadKey();
            #endregion

            #region LoadFile方法
            Assembly assmbly2 = Assembly.LoadFile(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");
            Console.WriteLine(assmbly2.FullName);
            Console.ReadKey();
            #endregion
        }
    }
}

這個三個方法語法都很簡單。現在來說說這個三個的區別,和優缺點。

LoadFrom和Load:LoadForm同一個程式集只載入一次,就算載入了不通的路徑相同的程式集,他也只能給你返回之前的程式集,LoadFrom只能用於載入不同標識的程式集, 也就是唯一的程式集, 不能用於載入標識相同但路徑不同的程式集。 

LoadFile和LoadForm:loadFile只會載入本身程式集,不會加在其引用的程式集,LoadForm和Load是會載入的其引用程式集。而且LoadFile同一個程式集只能載入一次。這個和LoadForm是一樣。

從性能上看 LoadForm沒有Load好,功能上也是load強一點。應用的時候如果loadFom和load都滿足需求,建議用load。

這幾個方法還幾個重載版本。由於本片只是基礎篇。篇幅不宜過多。想深入瞭解的小伙伴可以查詢MSN看看文檔。最詳細的說明就是文檔。但是文檔說的比較官方。結合大牛們寫的博客。更容易懂一點。

好了我們開始下一個階段了。程式集現在我們得了。我開始看看程式集裡面有些啥?

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {


            #region LoadFrom方法
            Assembly assmbly = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");
            Type[] types = assmbly.GetTypes();
            foreach (var item in types)
            {
                Console.WriteLine("類:"+item.Name);

            }
            Console.ReadKey();
            #endregion

        }
    }
}

assmbly.GetTypes()這個方法或獲取了所有類。
下麵我我展示下獲取欄位和方法的欄位
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {


            #region LoadFrom方法
            Assembly assmbly = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");
            Type[] types = assmbly.GetTypes();
            foreach (var classitem in types)
            {
                Console.WriteLine("類:"+ classitem.Name);
                foreach (var fileditem in classitem.GetFields())
                {
                    Console.WriteLine("欄位名:"+ fileditem.Name);
                }
                foreach (var methodItem in classitem.GetMethods())
                {
                    Console.WriteLine("方法名:"+methodItem.Name);
                }

            }
            Console.ReadKey();
            #endregion

        }
    }
}

 

可以看的出來我們將欄位和方法名都給反射出來了,但是有個問題就是我們父類的方法也給弄出來了。我們可以修改下。這個地方有個重載版本。GetMethods有個重載方法可以通過BindingFlags枚舉參數進行篩選父類的方法,或者其他的。 BindingFlags這個枚舉類型。這裡就不多講。未來我會慢慢補全。

簡單的就是這麼多了。反射能做好多事情,非常靈活。我們抽象工廠裡面就會用到反射。我們的工廠。就是通過反射創建出來。下麵我寫個demo演示下其作用。

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestDLL;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assmbly = Assembly.Load("TestDLL");
            SqlServerHelper helper =(SqlServerHelper)assmbly.CreateInstance("TestDLL.SqlServerHelper");
            helper.Query();
            Console.ReadKey();
        }
    }
}

小伙伴們可以發現。我們實例化了一個SqlServerHelper對象,但是我們沒有用正常的new方法,而是用了反射。這個時候有的小伙伴可能就會說這是脫褲子放屁,直接new多簡單。在這裡是沒錯,但是放在真正的項目里直接new是當時爽,需求變動就等著哭吧,比如說以後領導對你說,公司的資料庫不用SqlServer了,換成Oracle了。這是時候如果你是new的話還要改這裡的代碼,實際情況可能更複雜。但是用了我們的反射,這種煩惱就不會存在了。

CreateInstance("TestDLL.SqlServerHelper") 這個方法參數我們完全可以通過配置文件修改。這個類就不需要變了。我來演示下代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestDLL
{
    public interface IDBHelper
    {
        void Query();
    }
}

 先創建一個約定數據操作的介面 IDBHlper類。規定有一個Query方法,然後讓SqlServerHelper繼承這個介面,並且實現這個方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestDLL
{
    public class SqlServerHelper: IDBHelper
    {
        public void Query()
        {
            Console.WriteLine("這是測試");
        }
    }
}

 然後修改mian函數的方法

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <appSettings>
    <add key="Helper" value="TestDLL.SqlServerHelper"/>
  </appSettings>
</configuration>
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestDLL;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assmbly = Assembly.Load("TestDLL");
            string helperkey = ConfigurationManager.AppSettings["Helper"];
            IDBHelper helper =(IDBHelper)assmbly.CreateInstance(helperkey);
            helper.Query();
            Console.ReadKey();
        }
    }
}

這時候如果要將mian函數裡面helper切換成Oracle的只要添加一個繼承於IDBheper介面的類,然後實現方法,在修改配置文件指向這個類,然後就可以了。對於main函數我們是一點不需要動的,這是就是我們所有的高內聚低耦合。完成解耦。易於修改。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestDLL
{
    public class OracleHelper : IDBHelper
    {
        public void Query()
        {
            Console.WriteLine("我是Orcle資料庫");
        }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <appSettings>
    <add key="Helper" value="TestDLL.OracleHelper"/>
  </appSettings>
</configuration>
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestDLL;

namespace 反射Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assmbly = Assembly.Load("TestDLL");
            string helperkey = ConfigurationManager.AppSettings["Helper"];
            IDBHelper helper =(IDBHelper)assmbly.CreateInstance(helperkey);
            helper.Query();
            Console.ReadKey();
        }
    }
}

mian函數一點都沒有變化。運行查看結果

到了這個裡,我已經演示了一個反射應用場景了。其實VS本身就很多地方用了反射。比如。創建對象調用方法時候VS直接只能幫我們列出這個對象下的成員。這個就是通過反射。其實還有很多。等待大家去發現。反射應該屬於C#裡面的高級知識點了。目前所說的只是冰山一角。

Ok。講到這裡就結束了哈。

如果剛開始學習的小伙伴還有疑問的話,可以評論咱們一起學習。

如果哪位大牛隨便瞄到個錯誤,也請告之我,讓我能夠進步。

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 相對於版本1.0,多了很多方法, 比如,獲取文件的尾碼名,或修改尾碼名和一些文件的簡單操作。 文件複製到文件,文件複製到路徑,路徑下文件複製到新路徑下, 代碼如下,請享用: 組合併且封裝了File的一些方法,文件路徑就是File對象。 上邊都是字元流,至於線程開啟字元流的的運算,加油,你可以的。 ...
  • 五分鐘輕鬆學會管理項目開發環境。 在開發Python應用程式的時候,系統安裝的Python3只有一個版本:3.x。所有第三方的包都會被pip安裝到Python3的site-packages目錄下。 pycharm安裝可以在設置里進行管理。 如果我們要同時開發多個應用程式,每個應用可能需要各自擁有一套 ...
  • 最近忽然要用到在VerilogHDL中調用VHDL的模塊,從網上找了常式,把自己會忘掉的東西記在這裡,。 2選1多路復用器的VHDL描述:entity mux2_1 is port( dina : in bit; dinb : in bit; sel : in bit; dout : out bit ...
  • 首先說明同步與非同步,阻塞與非阻塞的問題: Asynchronous vs. Synchronous A method call is considered synchronous if the caller cannot make progress until the method returns ...
  • 一、寫在前面 最近做項目需要用到識別圖片中文字的功能,本來用的Tesseract這個寫的,不過效果不是很理想。 隨後上網搜了一下OCR介面,就準備使用騰訊雲、百度的OCR介面試一下效果。不過這個騰訊雲OCR就折騰了一天! 二、OCR-通用印刷體識別 首先附上文檔地址:OCR-通用印刷體識別 有兩種調 ...
  • DNS原理及其解析過程 為什麼需要DNS解析功能變數名稱為IP地址? 網路通訊大部分是基於TCP/IP的,而TCP/IP是基於IP地址的,所以電腦在網路上進行通訊時只能識別如“202.96.134.133”之類的IP地址,而不能認識功能變數名稱。我們無法記住10個以上IP地址的網站,所以我們訪問網站時,更多的是在 ...
  • 一、前言 Protocol Buffer(簡稱Protobuf或PB) 是一個跨平臺的消息交互協議,類似xml、json等 ;別隻會用Json和XML了,快來看看Google出品的Protocol Buffer!由於是Google的出品,我相信Protocol Buffer已經具備足夠的吸引力。如果 ...
  • 一、前言 我們都知道 protobuf是由Google開發的一款與平臺無關,語言無關,可擴展的序列化結構數據格式,可用做數據存儲格式, 通信協議 ! 在前面《.NET開源Protobuf-net組件修煉手冊》基礎入門課程中我們已經對Google的Protobuf有了一個比較全面的瞭解和認識,本次高級 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...