C#反射與特性(一):反射基礎
目錄
- C#反射與特性(一):反射基礎
- 1. 說明
- 1.1 關於反射、特性
- 2. 程式集操作
- 2.1 獲取 程式集物件(Assembly)
- 2.2 Assembly 使用
- 2.3 獲取程式集的方式
- 1. 說明
C#反射與特性(一):反射基礎
1. 說明
1.1 關於反射、特性
在 《C# 7.0 本質論》中,關於這方面的知識在 《第十八章 反射、特性和動態程式設計》;在《C# 7.0 核心技術指南》中,這部分內容在《第19章 反射和元資料》。
在這裡我們可以獲得一些關聯性很大的技術:反射、特性、元資料;
元資料:C# 編寫的程式編譯成一個程式集,程式集會包含元資料、編譯程式碼和資源。
元資料包含內容:
- 程式或類庫中每一個型別的描述;
- 清單資訊,包括與程式本身有關的資料,以及它依賴的庫;
- 在程式碼中嵌入的自定義特性,提供與特性所修飾的構造有關的額外資訊。
反射:在執行時檢查並使用元資料和編譯程式碼的操作稱為反射。
一個程式集包含的內容:
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
會載入一個程式集,然後自動載入此程式集依賴的其它程式