1. 程式人生 > >C#反射與特性(一):反射基礎

C#反射與特性(一):反射基礎

目錄

  • C#反射與特性(一):反射基礎
    • 1. 說明
      • 1.1 關於反射、特性
    • 2. 程式集操作
      • 2.1 獲取 程式集物件(Assembly)
      • 2.2 Assembly 使用
      • 2.3 獲取程式集的方式

C#反射與特性(一):反射基礎

1. 說明

1.1 關於反射、特性

在 《C# 7.0 本質論》中,關於這方面的知識在 《第十八章 反射、特性和動態程式設計》;在《C# 7.0 核心技術指南》中,這部分內容在《第19章 反射和元資料》。

[圖片來自 《C# 7.0 本質論》]

在這裡我們可以獲得一些關聯性很大的技術:反射、特性、元資料;

元資料:C# 編寫的程式編譯成一個程式集,程式集會包含元資料、編譯程式碼和資源。
元資料包含內容:

  • 程式或類庫中每一個型別的描述;
  • 清單資訊,包括與程式本身有關的資料,以及它依賴的庫;
  • 在程式碼中嵌入的自定義特性,提供與特性所修飾的構造有關的額外資訊。

反射:在執行時檢查並使用元資料和編譯程式碼的操作稱為反射。

一個程式集包含的內容:

[圖片來自 《C# 7.0 核心技術指南》]

2. 程式集操作

C# 編譯成的程式碼會生成到 .dll 或 .exe 檔案中,我們可以通過 Assembly 類,手動載入 程式集檔案,實現各種操作。

Assembly 類在 System.Reflection 名稱空間中。

《C# 7.0 核心技術指南》中,列出類 Assembly 類常用的屬性和方法:

接下來我們將通過程式碼操作,瞭解 Assembly 的使用方法。

建立一個控制檯專案,並設定程式集描述資訊。

2.1 獲取 程式集物件(Assembly)

微軟官方文件建議使用的載入程式集的方式:

  • 載入程式集的建議方法是使用 Load 方法,該方法標識要由其顯示名稱(例如 "b77a5c561934e089,Version = 2.0.0.0,Culture = 中立,PublicKeyToken =")載入的程式集。 該程式集的搜尋遵循執行時如何定位程式集中所述的規則。
  • 利用 ReflectionOnlyLoad 和 ReflectionOnlyLoadFrom 方法,你可以載入用於反射的程式集,但不能載入用於執行的程式集。 例如,可通過在32位平臺上執行的程式碼來檢查面向64位平臺的程式集。
  • 對於程式集必須按路徑標識的罕見方案,會提供 LoadFile 和 LoadFrom 方法。

一般獲取程式集有三種方式:

  • Assembly.Load()
  • Assembly.LoadFrom()
  • Assembly.LoadFile()

以下方法可以獲取到當前程式引用到的程式集:

AppDomain.CurrentDomain.GetAssemblies();

輸出

System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e

ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

System.Runtime, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

System.Runtime.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

2.1.1 執行時獲取程式集

通過正在執行的型別、函式等形式,去獲取程式集。

Assembly 類:
        public static Assembly? GetAssembly(Type type);
    
        public static Assembly GetCallingAssembly();
    
        public static Assembly? GetEntryAssembly();
    
        public static Assembly GetExecutingAssembly();
        
Type 類:
        {type}.Assembly

解析說明:

位置 函式 說明
Assembly GetAssembly(Type) 獲取在其中定義指定型別的當前載入的程式集
Assembly GetCallingAssembly() 返回方法(該方法呼叫當前正在執行的方法)的 Assembly
Assembly GetEntryAssembly() 獲取預設應用程式域中的程序可執行檔案。 在其他的應用程式域中,這是由 ExecuteAssembly(String)執行的第一個可執行檔案
Assembly GetExecutingAssembly() 獲取包含當前執行的程式碼的程式集
Type Assembly 返回一個型別所在的程式集

2.1.2 使用方法

            Assembly assem = typeof(Console).Assembly;
            Assembly ass = Assembly.GetExecutingAssembly();

2.1.3 從檔案載入程式集

函式 說明
LoadFrom(String) 已知程式集的檔名或路徑,載入程式集
LoadFrom(String, Byte[], AssemblyHashAlgorithm) 通過給定程式集檔名或路徑、雜湊值及雜湊演算法來載入程式集
LoadFrom(String, Evidence) 在給定程式集的檔名或路徑並提供安全證據的情況下,載入程式集
LoadFrom(String, Evidence, Byte[], AssemblyHashAlgorithm) 通過給定程式集檔名或路徑、安全證據、雜湊值及雜湊演算法來載入程式集

2.1.4 使用方法

Assembly ass = Assembly.LoadFrom(@"X:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Console.dll");

另外還有更多中載入程式集的方法,這些方法很偏僻,沒必要列出來(因為我不會)。

2.2 Assembly 使用

獲得 Assembly 物件後,就可以進行一系列的騷操作。

常用的 Assembly 函式可以檢視圖三。

先設定兩個 Assembly 物件

            Assembly assemA = typeof(Console).Assembly;
            Assembly assemB = Assembly.GetExecutingAssembly();

2.2.1 獲取程式集完全限定名稱

            Console.WriteLine("程式集完全限定名");
            Console.WriteLine(assemA.FullName);
            Console.WriteLine(assemB.FullName);
程式集完全限定名
System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

裡面有個 PublicKeyToken 屬性,前面我們介紹了 Assembly 獲取程式集的方式,通過 PublicKeyToken ,我們也可以使用 Load 來載入程式集。

但是你可以看到上面的輸出, System.Console 有 PublicKeyToken 值,但是自己建立的專案 ConsoleApp4 沒有。

2.2.2 AssemblyName

AssmblyName 是用來完整描述程式集的型別。

AssmblyName 是用來獲取 程式集 各種資訊的類,本身不具有操作功能,僅用於獲取程式集的元資料資訊。

AssmblyName 例項可以使用 Assembly 的 GetName() 方法獲取。

屬性 說明
CodeBase 獲取或設定程式集的 URL 位置。
ContentType 獲取或設定指示程式集包含的內容型別的值。
CultureInfo 獲取或設定程式集支援的區域性。
CultureName 獲取或設定與此程式集關聯的區域性名稱。
EscapedCodeBase 獲取 URI,包括表示基本程式碼的轉義符。
Flags 獲取或設定該程式集的屬性。
FullName 獲取程式集的全名(也稱為顯示名稱)。
HashAlgorithm 獲取或設定程式集清單使用的雜湊演算法。
KeyPair 獲取或設定用於為程式集建立強名稱簽名的加密公鑰/私鑰對。
Name 獲取或設定程式集的簡單名稱。 這通常(但不一定)是程式集的清單檔案的檔名,不包括其副檔名。
ProcessorArchitecture 獲取或設定一個值,該值標識可執行檔案的目標平臺的處理器和每字位數。
Version 獲取或設定程式集的主版本號、次版本號、內部版本號和修訂號。
VersionCompatibility 獲取或設定與程式集同其他程式集的相容性相關的資訊。
            AssemblyName assemNameA = assemA.GetName();
            AssemblyName assemNameB = assemB.GetName();

            Console.WriteLine("程式集名稱: {0}", assemNameA.Name);
            Console.WriteLine("程式集名稱: {0}", assemNameB.Name);

            // 版本
            Console.WriteLine("\nVersion: {0}.{1}",
                assemNameA.Version.Major, assemNameA.Version.Minor);
            Console.WriteLine("Version: {0}.{1}",
    assemNameB.Version.Major, assemNameB.Version.Minor);

            // 程式集的物理檔案位置
            Console.WriteLine("\nAssembly CodeBase:{0}", assemA.CodeBase);
            Console.WriteLine("\nAssembly CodeBase:{0}", assemB.CodeBase);

輸出資訊

程式集名稱: System.Console
程式集名稱: ConsoleApp4

Version: 4.1
Version: 1.0

Assembly CodeBase:file:///x:/Program Files/dotnet/shared/Microsoft.NETCore.App/3.0.1/System.Console.dll

Assembly CodeBase:file:///X:/Users/whuanle/source/repos/ConsoleApp4/ConsoleApp4/bin/Debug/netcoreapp3.0/ConsoleApp4.dll

除了 GetName(),Assembly 類還提供了許多與成員的有關程式集的資訊。 例如:

  • GetName 方法返回一個 AssemblyName 物件,該物件提供對程式集顯示名稱的各個部分的訪問。
  • GetCustomAttributes 方法列出應用於程式集的特性。
  • GetFiles 方法提供對程式集清單中的檔案的訪問。
  • GetManifestResourceNames 方法提供程式集清單中的資源的名稱。

2.3 獲取程式集的方式

上面說到,載入程式集的方式一般使用三種方法:

  • Assembly.Load()
  • Assembly.LoadFrom()
  • Assembly.LoadFile()

上面已經演示執行時獲取和 LoadFrom 兩種獲取方式。

下面來繼續介紹 Assembly.Load()Assembly.LoadFile()

2.3.1 Assembly.Load()

Assembly.Load() 以強型別的方式去載入程式集,

強名稱和程式集簽名 指的是 程式集具有唯一的和不可更改的標識。

何以為強型別?通過在清單中新增如下的兩種元資料實現:

  • 屬於該程式集作者的唯一編號;

  • 程式集簽名後的雜湊值,以證實該程式集是由持有其唯一編號的作者生成;

關於這部分內容可以參考 《C# 7.0 核心技術指南》的《18.2 強名稱和程式集簽名》部分,這裡不再贅述。

Assembly.Load() 載入程式集,同時可以自動載入程式集引用到的其它程式集,並且不會造成重複載入問題。

使用示例:

            Assembly assemA = Assembly.Load("System.Console");
            Assembly assemB = Assembly.Load("ConsoleApp4");
            Assembly assemC = Assembly.Load("System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");

參考:Assembly.Load 詳解(c#)

地址:https://www.cnblogs.com/weifeng123/p/8855629.html

參考:深入瞭解C#反射中Assembly.Load()、Assembly.LoadFrom()、Assembly.LoadF ile ()方法

地址:https://blog.csdn.net/xuchen_wang/article/details/92773260

2.3.2 Assembly.LoadFile()

Assembly.LoadFile()Assembly.LoadFrom 的使用方法一致。

區別: Assembly.LoadFile()只會載入指定的一個程式集; Assembly.LoadFrom 會載入一個程式集,然後自動載入此程式集依賴的其它程式