C#程式集和反射的圖文代碼詳解

來源:互聯網
上載者:User
本文主要介紹了C# 程式集和反射的相關知識。具有一定的參考價值,下面跟著小編一起來看下吧

這裡我又嘮叨幾句,大家在學習的時候,如看書或者看視頻時覺得非常爽,因為感覺基本都看得懂也都挺容易的,其實看懂是一回事,你自己會動手做出來是一回事,自己能夠說出來又是另一回事了。應該把學到的東西變成自己的東西,而不是依樣畫瓢。

在說反射之前,我們先來瞭解一下什麼是程式集?

程式集

程式集是.net中的概念,程式集可以看作是給一堆相關類打一個包,相當於java中的jar包。

程式集包含:

  • 資源檔

  • 類型中繼資料(描述在代碼中定義的每一類型和成員,二進位形式)

  • IL代碼(這些都被封裝在exe或dll中)

exe與dll的區別。

exe可以運行,dll不能直接運行,因為exe中有一個main函數(入口函數)。

類型中繼資料這些資訊可以通過AssemblyInfo.cs檔案來自訂。在每一個.net項目中都存在一個AssemblyInfo.cs檔案,代碼格式:

using System.Reflection;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;// 有關程式集的常規資訊通過以下// 特性集控制。更改這些特性值可修改// 與程式集關聯的資訊。[assembly: AssemblyTitle("ReflectedDemo")][assembly: AssemblyDescription("")][assembly: AssemblyConfiguration("")][assembly: AssemblyCompany("")][assembly: AssemblyProduct("ReflectedDemo")][assembly: AssemblyCopyright("Copyright  2017")][assembly: AssemblyTrademark("")][assembly: AssemblyCulture("")]// 將 ComVisible 設定為 false 使此程式集中的類型// 對 COM 組件不可見。 如果需要從 COM 訪問此程式集中的類型,// 則將該類型上的 ComVisible 特性設定為 true。[assembly: ComVisible(false)]// 如果此項目向 COM 公開,則下列 GUID 用於類型庫的 ID[assembly: Guid("7674d229-9929-4ec8-b543-4d05c6500863")]// 程式集的版本資訊由下面四個值組成: ////   主要版本//   次版本 //   組建編號//   修訂編號//// 可以指定所有這些值,也可以使用“組建編號”和“修訂編號”的預設值,// 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")][assembly: AssemblyVersion("1.0.0.0")][assembly: AssemblyFileVersion("1.0.0.0")]

這些資訊在哪裡體現呢?就在我們程式集的屬性當中進行體現

我們平時在安裝一些CS用戶端程式的時候,在安裝目錄下面會看見許多的組件檔。

使用程式集的好處

  • 程式中只引用必須的程式集,減小程式的尺寸。

  • 程式集可以封裝一些代碼,只提供必要的提供者。

  • 方便擴充。

如何添加程式集的引用?

直接添加程式集路徑或者添加解決方案中的項目引用。

當我們需要擴充一個程式的時候,你可能會直接在原有的項目中進行添加,那這樣的話,如果你的這些代碼想共用給別人使用呢?你就可以打包成一個程式集,然後別人只要通過引用你這個程式集就可以進行擴充了。像我們常見的.net第三方架構庫,如log4net、unity等等。

注意:不能添加循環參考

什麼是添加循環參考?就是說A項目如果添加了B項目的項目引用,那麼此時B項目不能再添加A項目的項目引用,也就是說添加項目引用時,必須是單向的,像我們常見的三層架構之間的項目引用。

反射

關於反射,你只要是做.net開發,你就一定天天在用。因為VS的智能提示就是通過應用了反射技術來實現的,還有我們常用的反編譯神器Reflector.exe,看它的名字就知道了。項目中比較常見的,是通過結合設定檔來動態執行個體化對象,如切換資料庫執行個體,或者Sprint.net的通過設定檔來實現依賴注入等。

反射技術其實就是動態擷取程式集的中繼資料的功能,反射通過動態載入dll,然後對其進行解析,從而建立對象,調用成員。

Type是對類的描述,Type類是實現反射的一個重要的類,通過它我們可以擷取類中的所有資訊,包括方法、屬性等。可以動態調用類的屬性、方法。

反射的出現讓建立對象的方式發生了改變,因為過去面完建立對象都是直接通過new。

dll裡面有兩部分東西:IL中繼語言和metadate元素據。

在.NET中反射用到命名空間是System.Reflection,這裡我先通過一個Demo來看反射能做些什麼

1、 建立控制台項目ReflectedDemo

2、 建立類庫項目My.Sqlserver.Dal

建立兩個類SqlServerHelper和SqlCmd,前者為共有類,後者為私人類

namespace My.Sqlserver.Dal{  public class SqlServerHelper  {    private int age = 16;    public string Name { get; set; }    public string Query()    {      return string.Empty;    }  }  class SqlCmd  {  }}

3、 項目ReflectedDemo,添加My.Sqlserver.Dal的項目引用,我這樣做的目的是為了方便項目ReflectedDemo中的bin目錄中時刻存在My.Sqlserver.Dal.dll程式集。

using System;using System.Reflection;namespace ReflectedDemo{  class Program  {    static void Main(string[] args)    {      //載入組件檔,在bin目錄中尋找      Assembly assembly = Assembly.Load("My.Sqlserver.Dal");      Console.WriteLine("----------------Modules----------------------");      var modules = assembly.GetModules();      foreach(var module in modules)      {        Console.WriteLine(module.Name);      }      Console.WriteLine("----------------Types----------------------");      var types = assembly.GetTypes(); //擷取程式集中所有的類型,包括公開的和不公開的      foreach(var type in types)      {        Console.WriteLine(type.Name);        Console.WriteLine(type.FullName);        var members= type.GetMembers(); //擷取Type中所有的公用成員        Console.WriteLine("----------------members----------------------");        foreach(var m in members)        {          Console.WriteLine(m.Name);        }      }      Console.WriteLine("----------------GetExportedTypes----------------------");      var exportedTypes = assembly.GetExportedTypes(); //擷取程式集中所有的公用類型      foreach(var t in exportedTypes)      {        Console.WriteLine(t.Name);      }      Console.WriteLine("----------------GetType----------------------");      var typeName= assembly.GetType("SqlServerHelper");//擷取程式集中指定名稱的類型對象      Console.WriteLine(typeName.Name);    }  }}

動態建立對象

通過ass.CreateInstance(string typeName) 和Activator.CreateInstance(Type t)方法

他們之間的區別

ass.CreateInstance(string typeName) 會動態調用類的無參建構函式建立一個對象,傳回值就是建立的對象,如果沒有無參建構函式就會報錯。


Assembly assembly = Assembly.Load("My.Sqlserver.Dal");object obj = assembly.CreateInstance("My.Sqlserver.Dal.SqlServerHelper");Console.WriteLine(obj.GetType().ToString());

如果我們來修改SqlServerHelper類的代碼,添加如下建構函式:

    public SqlServerHelper(int age)    {      this.age = age;    }

這個時候再來運行建立執行個體的代碼就會報錯了,而編譯時間是不報錯的。

所以我們一般推薦使用Activator.CreateInstance方法來建立反射對象,因為此方法有許多重載,支援將參數傳遞給建構函式。

此時再調用就不會出現異常了。

Type類中有三個用得比較多的方法:

  • bool IsAssignableFrom(Type t):是否可以從t賦值,判斷當前的類型變數是不是可以接受t類型變數的賦值。

  • bool IsInstanceOfType(object o):判斷對象o是否是當前類的執行個體,當前類可以是o的類、父類、介面

  • bool IsSubclassOf(Type t):判斷當前類是否是t的子類

Type類中還有一個IsAbstract屬性:判斷是否為抽象的,包含介面。

它們常用的原因是我們通過反射可以取到的東西太多了,我們需要對資料進行過濾。

添加類BaseSql,讓類SqlServerHelper繼承自BaseSql

然後查看調用代碼:

      bool result = typeof(BaseSql).IsAssignableFrom(typeof(SqlServerHelper));      Console.WriteLine(result);


   SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);   bool result = typeof(SqlServerHelper).IsInstanceOfType(_SqlServerHelper);   Console.WriteLine(result);


      SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);      bool result = typeof(SqlServerHelper).IsSubclassOf(typeof(BaseSql));      Console.WriteLine(result);

項目中常用的利用反射來動態切換資料庫Demo:

建立類庫項目My.Sql.IDal,並添加介面ISqlHelper。通過介面來實現資料庫操作的類的解耦,因為介面是抽象的。

  public interface ISqlHelper  {    string Query();  }

添加類庫項目My.MySql.Dal,並新增類MySqlHelper.cs

My.Sqlserver.Dal、My.MySql.Dal項目分別添加對項目My.Sql.IDal的引用。讓SqlServerHelper繼承自介面ISqlHelper

public class MySqlHelper : ISqlHelper  {    public string Query()    {       return this.GetType().ToString();    }  }  public class SqlServerHelper :ISqlHelper  {    private int age = 16;    public string Name { get; set; }    public string Query()    {      return this.GetType().ToString();    }  }

添加App.config配置項

 <appSettings>  <add key="DBName" value="My.Sqlserver.Dal,SqlServerHelper"/> </appSettings>

ReflectedDemo項目中Program.cs調用代碼:

string str = ConfigurationManager.AppSettings["DBName"];      string strAssembly = str.Split(',')[0];      string strClass=str.Split(',')[1];      Assembly assembly = Assembly.Load(strAssembly);      Type t = assembly.GetType(strAssembly + "." + strClass);      ISqlHelper obj = Activator.CreateInstance(t) as ISqlHelper;      Console.WriteLine(obj.Query());

這樣每次需要切換資料庫時,只要修改設定檔就可以了。

項目結構:

注意:反射雖然很強大,但卻是比較耗效能的,所以一般和緩衝結合起來使用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.