目錄
? ? ? ? 本文末尾有相關類中的總結,如有需要直接跳到最后即可
前置知識:
1、程序集(Assembly)
2、元數據(Metadata)
3、中間語言(IL, Intermediate Language)
????????中間語言(IL)是.NET編譯器(如C#、VB.NET編譯器)生成的平臺無關代碼。它介于高級語言(如C#)和機器碼之間,是.NET程序集的執行邏輯核心。
特點
生命周期:
4、 資源(Resources)
定義:資源是程序集中嵌入的非代碼數據,例如圖像、音頻、字符串、本地化文件(多語言文本)、配置文件等。它們與代碼邏輯分離,便于動態管理和復用。
類型
作用
訪問方式
5、清單(Manifest)
定義:清單是程序集的元數據,描述了程序集自身的身份信息、依賴關系、安全權限等。它是程序集的“自述文件”,確保 CLR 能正確加載和執行程序集。
內容
(1)程序集標識:??
(2)依賴項:
(3)文件列表:
(4)安全權限:
作用
示例
一、什么是反射
具體表現:
核心原理
二、補充:平常寫完代碼執行和動態操作有什么區別?
常規代碼執行(靜態綁定):
反射動態操作(動態綁定):
核心區別:
二、反射的作用的場景
1. 插件系統與動態擴展
2. 依賴注入與框架開發
3. 序列化與反序列化
????????反射存在的必要性??
三、和反射相關的重要類和語法
1.?Type?類
(1)獲取?Type?對象的三種方式
? ?1)typeof?運算符直接通過類型名稱獲取:
? ? ? ? 2)實例的?GetType()?方法通過對象實例獲取:
? 3)Type.GetType()?靜態方法通過類型名稱字符串(需完整命名空間)獲取:注意類名必須包含命名空間 不然找不到
(2)Type?類的重要方法和屬性
???????1)獲取某一個類的公共成員
???????2)獲取某一個類公共構造函數并調用
①無參構造函數的調用
步驟:
② 有參構造函數的調用
步驟:
③ 處理多個構造函數的情況
3)獲取某一個類公共成員變量
GetFields()獲取所有公共字段:
?獲取指定名稱的公共字段
獲取和設置指定字段的值:
4)獲取某一個類公共方法
GetMethods()獲取所有公共方法:?MethodInfo 是方法的反射信息
①基本調用:實例方法
②處理重載方法:就是什么類型傳什么參數
?③調用靜態方法
5)枚舉操作
6)事件操作
7)屬性操作
8)接口操作
2.?Assembly?類
(1)加載程序集的方式
對比總結
(2)獲取程序集中的類型
(3)訪問清單信息
3.?Activator?類
(1)創建無參實例
語法:
注意事項
(2)創建帶參實例
示例:
注意事項
(3)處理私有構造函數
語法:
反射核心類總結表
? 引言:? ? ??
????????在.NET生態中,反射(Reflection)是一種突破靜態語言限制的動態編程機制,賦予程序在運行時“自省”與“操控”代碼的能力。通過反射,開發者無需在編譯時綁定具體類型,即可動態加載程序集、解析類型信息、創建對象實例及調用方法。這種能力為插件系統、依賴注入框架、序列化工具等復雜場景提供了核心支持。例如,主程序可通過反射動態加載未知插件,ORM框架能自動映射數據庫字段到對象屬性,依賴注入容器可解析服務間的復雜依賴鏈。然而,反射的靈活性也伴隨性能開銷和復雜性挑戰。理解反射的核心原理、關鍵類(如Type
、Assembly
)及適用場景,是掌握現代.NET高級開發的關鍵。本文將從程序集、元數據等基礎概念出發,系統剖析反射的實現機制,并通過實戰示例展示其強大能力與使用邊界。
? ? ? ? 本文末尾有反射相關類中的總結,如有需要直接跳到最后即可
前置知識:
1、程序集(Assembly)
程序集時經由編譯器編譯生成的可執行文件 供進一步編譯執行的那個中間產物
在Windows系統中他一般表現為.exe(可執行文件)或者.dll文件(庫文件)說人話:
程序集就是我們寫的一個代碼集合 我們現在寫的所有代碼
最終都會被編譯成程序集供別人使用
比如一個代碼庫文件.dll或者 一個可執行文件.exe
定義:程序集是.NET應用程序的基本構建塊,是編譯后的代碼邏輯單元。它可以是一個可執行文件(.exe
)或動態鏈接庫(.dll
)。
可以在這里看:
等會我們也可以通過代碼進行查看
組成:
中間語言(IL, Intermediate Language):由編譯器生成的與平臺無關的代碼。
元數據(Metadata):描述程序集中包含的類型、方法、屬性等信息。
資源(Resources):如圖像、字符串、本地化文件等。
清單(Manifest):程序集的元數據,包括版本、依賴項、安全權限等。
作用:
是.NET應用程序部署和版本控制的基本單元。
支持代碼共享和重用。
2、元數據(Metadata)
定義:元數據是描述程序結構和內容的數據。在.NET中,元數據記錄了程序集中的類型(類、接口、結構體)、成員(方法、屬性、字段)、依賴關系等信息。
內容:
類型名稱、命名空間、可見性(public/private)。
方法的簽名(參數、返回值)。
屬性的類型和訪問級別。
繼承關系(基類、接口實現)。
作用:
支持反射機制。
確保類型安全(CLR通過元數據驗證代碼)。
為調試工具、IDE(如Visual Studio)提供智能感知功能。
3、中間語言(IL, Intermediate Language)
定義:
????????中間語言(IL)是.NET編譯器(如C#、VB.NET編譯器)生成的平臺無關代碼。它介于高級語言(如C#)和機器碼之間,是.NET程序集的執行邏輯核心。
特點
平臺無關性:IL 不依賴具體硬件或操作系統,可在任何支持 .NET 運行時(CLR)的環境中運行。
面向堆棧:IL 是基于堆棧的指令集(類似匯編語言),操作通過壓棧和彈棧完成。
安全性:CLR 在運行前會驗證 IL 代碼的安全性(如類型安全、內存安全)。
生命周期:
-
編譯:C# 代碼 → 編譯器生成 IL(保存在?
.dll
?或?.exe
?文件中)。 -
執行:CLR 通過?JIT 編譯器(Just-In-Time Compiler)?將 IL 轉換為當前平臺的本地機器碼(Native Code)。
C# → IL → JIT編譯 → 機器碼 → CPU執行
4、 資源(Resources)
定義:資源是程序集中嵌入的非代碼數據,例如圖像、音頻、字符串、本地化文件(多語言文本)、配置文件等。它們與代碼邏輯分離,便于動態管理和復用。
類型
嵌入式資源(Embedded Resource):直接編譯到程序集中,通過代碼訪問。
鏈接資源(Linked Resource):在程序集中記錄資源路徑,但文件獨立存在。
作用
本地化:存儲不同語言的字符串,支持應用程序國際化。
靜態內容管理:如圖標、默認配置文件。
動態加載:運行時按需加載資源(如插件系統中的圖片)。
訪問方式
在 C# 中通過?ResourceManager
?類訪問資源:
// 假設資源文件名為 Resources.resx
ResourceManager rm = new ResourceManager("MyApp.Resources", Assembly.GetExecutingAssembly());
string greeting = rm.GetString("Greeting"); // 獲取鍵為 "Greeting" 的字符串
5、清單(Manifest)
定義:清單是程序集的元數據,描述了程序集自身的身份信息、依賴關系、安全權限等。它是程序集的“自述文件”,確保 CLR 能正確加載和執行程序集。
內容
(1)程序集標識:??
名稱(Name)
版本號(Version,格式:主版本.次版本.生成號.修訂號)
文化信息(Culture,用于本地化資源)。
公鑰令牌(Public Key Token,用于強名稱程序集)。
(2)依賴項:
引用的其他程序集(名稱、版本、公鑰)。
(3)文件列表:
程序集包含的所有文件(如主模塊、資源文件)。
(4)安全權限:
程序集運行所需的權限(如文件訪問、網絡訪問)。
作用
版本控制:避免“DLL Hell”(同一 DLL 不同版本的沖突)。
依賴解析:CLR 根據清單查找并加載依賴項。
部署簡化:自包含的元數據無需注冊表(與 COM 不同)。
示例
一個程序集的清單可能包含:
<assembly><name>MyLibrary</name><version>1.0.0.0</version><culture>neutral</culture><publicKeyToken>a1b2c3d4e5f6g7h8</publicKeyToken><dependency><dependentAssembly><name>Newtonsoft.Json</name><version>13.0.0.0</version></dependentAssembly></dependency>
</assembly>
小結:
????????在.NET框架中,程序集(Assembly)作為應用程序的基本構建單元,本質上是一個邏輯容器,整合了四個核心組成部分:中間語言(IL)代碼、元數據(Metadata)、資源(Resources)和清單(Manifest)。這些內容相互協作,共同支撐程序的編譯、運行和動態行為。程序集中的IL代碼是由高級語言(如C#)編譯生成的平臺無關指令,定義了程序的執行邏輯;元數據則如同“代碼的說明書”,詳細描述了IL中的類型(如類、接口)、成員(如方法、屬性)及其依賴關系,確保運行時環境能夠理解并安全執行代碼。清單作為程序集的“身份標識”,記錄了程序集的名稱、版本、依賴項和安全權限等頂層信息,是CLR(公共語言運行時)加載和驗證程序集的依據。資源則嵌入非代碼數據(如圖像、本地化文本),通過資源管理器動態加載,與代碼邏輯解耦。
????????四者之間的關系緊密且功能互補。元數據不僅為IL代碼中的類型和方法提供結構化描述(例如方法的參數和返回類型),還指導JIT(即時)編譯器將IL轉換為機器碼時進行類型驗證和內存分配。清單作為元數據的擴展,專注于程序集級別的信息管理,例如聲明依賴的外部程序集(如mscorlib.dll
),使得CLR能夠按需加載這些依賴項。資源文件雖然在邏輯上獨立于代碼,但其存在和路徑信息通常通過清單記錄,而IL代碼則通過ResourceManager
類在運行時動態訪問這些資源(例如加載多語言字符串)。在程序的生命周期中,編譯階段將源代碼轉換為IL代碼,同時生成元數據和清單,并將資源文件嵌入程序集;運行階段,CLR首先讀取清單以加載所有依賴項,接著利用元數據驗證類型安全并指導JIT編譯,最終通過反射機制或資源管理器實現動態類型操作和資源加載。這種分層協作機制不僅保障了.NET應用程序的跨平臺性和安全性,還為高級功能(如反射、插件系統)提供了底層支持。
一、什么是反射
????????反射(Reflection)是 .NET 框架提供的一種動態編程機制,允許程序在運行時(而非編譯時)獲取程序集、類型及其成員的信息,并能夠動態創建對象、調用方法、訪問屬性或字段。
簡單來說,反射讓代碼可以像“鏡子”一樣,在運行時“照見”自身的結構,并基于此結構動態操作代碼邏輯,而無需在編譯時硬編碼類型信息。
????????反射的核心意義在于賦予程序運行時動態操作代碼的能力,使得代碼不再被編譯時的靜態結構所束縛。通過反射,程序可以在不預先知曉類型細節的情況下,動態加載、解析和操作代碼邏輯,從而應對復雜多變的場景需求。
????????“運行時動態操作代碼的能力”指的是程序在運行階段(而非編寫或編譯時)能夠動態地分析、修改或調用代碼邏輯。這種能力允許程序在事先不知道具體類型或方法的情況下,根據運行時的條件(如配置文件、用戶輸入)靈活地執行操作。就比如你給你的瀏覽器裝一個插件,但是你并沒有關閉瀏覽器對吧,只是可能在讓你裝完之后重啟一下,這就是動態的執行預先設定好的操作。
具體表現:
動態加載程序集:例如,一個主程序在運行時加載一個未知的插件(
.dll
?文件)。動態創建對象:根據字符串形式的類名(如?
"MyApp.Logger"
)創建實例。動態調用方法:通過方法名稱字符串(如?
"Calculate"
)調用方法,而無需在代碼中硬編碼方法名。訪問私有成員:繞過編譯時的訪問限制,讀取或修改類的私有字段。
核心原理
????????反射基于程序集中的元數據(Metadata)實現。元數據是嵌入在程序集中的結構化信息,記錄了以下內容:
類型的定義(類、接口、結構體、枚舉)。
類型的成員(方法、屬性、字段、事件)。
方法的參數和返回值類型。
程序集的版本、依賴項等。
????????通過反射 API,程序可以讀取這些元數據,并利用它們動態執行操作。例如,即使不知道某個類的具體定義,也能通過反射創建它的實例并調用其方法。
一個最簡單的示例:
假設有一個插件系統,主程序需要加載不同開發者編寫的插件:
// 主程序不知道具體的插件類名
string pluginName = ReadPluginNameFromConfig(); // 例如返回 "MyPlugin.Logger"
Assembly assembly = Assembly.LoadFrom(pluginName + ".dll");
Type pluginType = assembly.GetType(pluginName);
object plugin = Activator.CreateInstance(pluginType);// 動態調用插件的 "Run" 方法
MethodInfo runMethod = pluginType.GetMethod("Run");
runMethod.Invoke(plugin, null);
????????這里,主程序在編譯時完全不知道?MyPlugin.Logger
?類的存在,而是通過反射在運行時動態加載和調用。?
二、補充:平常寫完代碼執行和動態操作有什么區別?
常規代碼執行(靜態綁定):
特點:代碼在編譯時已經確定了所有類型、方法和調用關系。
示例:
// 直接調用已知類的方法
MyLogger logger = new MyLogger();
logger.Log("Hello"); // 編譯時明確知道 MyLogger 類和 Log 方法
?優勢:
性能高(編譯器優化)。
代碼可讀性強(類型和方法名直接可見)。
限制:
無法適應動態需求(如插件、動態配置)。
必須提前知道所有類型和依賴。
反射動態操作(動態綁定):
特點:代碼在運行時動態解析類型和調用方法。
示例:
這里看不懂沒關系,只是一個簡單的示例而已,我們后面會好好講里面的門道的。
// 通過反射動態調用方法
string className = "MyLogger";
string methodName = "Log";Type type = Type.GetType(className);
object obj = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod(methodName);
method.Invoke(obj, new object[] { "Hello" });
優勢:
靈活應對未知類型(如插件、動態配置)。
支持通用框架開發(如依賴注入、ORM)。
缺點:
性能較低(需運行時解析元數據)。
代碼復雜度高,可維護性差。
核心區別:
維度 | 常規執行 | 反射動態操作 |
---|---|---|
綁定時機 | 編譯時確定類型和方法 | 運行時動態解析類型和方法 |
靈活性 | 低(依賴編譯時已知類型) | 高(適應未知類型和動態需求) |
性能 | 高(編譯器優化) | 低(運行時解析開銷) |
使用場景 | 固定邏輯、核心業務代碼 | 插件系統、框架、動態配置 |
小結:
運行時動態操作代碼:在程序運行階段動態解析和調用類型、方法,適應靈活需求。
與常規執行的區別:靜態綁定在編譯時固定,動態綁定在運行時解析,犧牲性能換取靈活性。
編譯時的靜態結構:代碼在編譯階段確定的類型和成員定義,是元數據和 IL 的基礎。
????????反射的核心價值在于彌合靜態語言在動態場景下的不足,但其使用需權衡靈活性與性能。
二、反射的作用的場景
隨便列舉了幾個可能的場景,來加深對反射的理解
1. 插件系統與動態擴展
????????場景:開發一個支持插件(如瀏覽器擴展、IDE插件)的應用程序,允許第三方開發者編寫功能模塊,主程序在運行時動態加載這些插件。
????????為什么需要反射:
????????動態加載:主程序無法預先知道插件的具體類型和實現,需通過反射加載插件程序集(
.dll
)。????????接口解耦:插件可能實現某個約定接口(如
IPlugin
),反射可以遍歷程序集,找到所有實現該接口的類并實例化。????????示例:Visual Studio 的擴展功能、游戲 Mod 系統。
2. 依賴注入與框架開發
????????場景:構建一個依賴注入(DI)框架(如 ASP.NET Core 的內置容器),自動解析服務類型并注入依賴對象。
????????為什么需要反射:
類型解析:框架需要根據配置或約定(如構造函數參數)動態創建對象實例。
生命周期管理:反射用于檢查類型的依賴關系樹,確保單例、瞬態等生命周期的正確實現。示例:ASP.NET Core 的?
Startup
?類中通過反射掃描并注冊服務。
3. 序列化與反序列化
????????場景:將對象轉換為 JSON/XML(如?Newtonsoft.Json
),或從數據庫讀取數據填充到對象(ORM 框架)。
????????為什么需要反射:
動態讀寫屬性:序列化庫需要遍歷對象的所有屬性(包括私有字段),反射可以繞過編譯時的訪問限制。
處理未知類型:反序列化時,根據 JSON 中的類型名稱動態創建對象。
示例:
JsonConvert.SerializeObject()
?內部使用反射遍歷對象屬性。
小結:
????????反射存在的必要性??
突破靜態語言的限制
????????C#、Java 等靜態語言在編譯時要求類型必須明確,但實際開發中常需處理“未知類型”(如插件、動態配置)。反射填補了這一鴻溝,允許在運行時動態解析類型。
提升代碼的靈活性與擴展性
????????反射使程序能夠“動態適應變化”。例如,插件系統無需重新編譯主程序即可擴展功能。框架開發者通過反射實現通用邏輯(如依賴注入、ORM),減少硬編碼。
支持高級編程范式自省(Introspection):
????????程序可以檢查自身結構,用于調試、序列化等場景。
應對復雜業務需求:
????????在需要高度動態行為的場景(如規則引擎、工作流系統),反射是實現靈活業務邏輯的核心技術。
元編程:
????????反射允許代碼操作代碼本身,實現 AOP、動態代理等高級模式。
反射的缺點:
性能開銷:反射操作比直接調用慢數十倍,頻繁調用需緩存?
MethodInfo
?等元數據。代碼可維護性:過度使用反射會導致代碼晦澀難懂,增加調試難度。
安全性:反射可繞過訪問限制,可能破壞封裝性,需嚴格控制權限。
????????反射是 .NET 中實現動態編程的基石,它的存在解決了靜態語言在運行時動態操作代碼的難題。無論是構建靈活可擴展的框架(如 ASP.NET Core、Entity Framework),還是開發插件化應用、高效測試工具,反射都發揮著不可替代的作用,因為反射可以在程序編譯后獲得信息,所以他提高了程序的拓展性和靈活性:
1.程序運行時得到所有的元數據 包括元數據的特性
2.程序運行時實例化對象 操作對象
3.程序運行時創建新的對象 用這些對象執行任務
????????然而,需在靈活性與性能、可維護性之間權衡,避免濫用反射導致代碼質量下降。理解反射的適用場景與代價,是將其價值最大化的關鍵。?
三、和反射相關的重要類和語法
? ? ? ? 好的,接下來咱們來學習如何進行反射的操作。下面操作需要引用命名空間 using System.Reflection
首先定義一個類來被用:
using System;public class MyClass
{// 公共屬性public string Message { get; set; }public int Value { get; }// 無參構造函數public MyClass(){Message = "默認消息";Console.WriteLine("無參構造函數被調用!");}// 有參構造函數public MyClass(string message, int value){Message = message;Value = value;Console.WriteLine($"有參構造函數被調用!Message={message}, Value={value}");}// 公共方法public void PrintInfo(){Console.WriteLine($"Message: {Message}, Value: {Value}");}
}
1.?Type
?類
??Type
?類是反射的核心,用于表示類型(類、接口、結構體、枚舉等),提供了訪問類型元數據的所有方法。
Type(類的信息類)
他是反射功能的基礎
他是訪問元數據的主要方式
使用Type的成員獲取有關類型申明的信息
有關類型的成員(如構造函數,方法,字段,屬性和類的事件)
(1)獲取?Type
?對象的三種方式
? ?1)typeof
?運算符
直接通過類型名稱獲取:
Type type1 = typeof(MyClass); // 靜態類型
? ? ? ? 2)實例的?GetType()
?方法
通過對象實例獲取:
MyClass obj = new MyClass();
Type type2 = obj.GetType();
? 3)Type.GetType()
?靜態方法
通過類型名稱字符串(需完整命名空間)獲取:注意類名必須包含命名空間 不然找不到
Type type3 = Type.GetType("Namespace.MyClass"); // 需包含程序集信息
Type type4 = Type.GetType("System.Int32");
(2)Type
?類的重要方法和屬性
????????獲取程序集信息:
? ? ?Assembly
?屬性
? 獲取類型所屬的程序集:
Assembly assembly = typeof(MyClass).Assembly;
Console.WriteLine(assembly.FullName); // 輸出程序集名稱和版本//例如
Console.WriteLine(type.Assembly);
Console.WriteLine(type2.Assembly);
Console.WriteLine(type3.Assembly);
???????1)獲取某一個類的公共成員
-
GetMembers()
獲取所有公共成員(包括方法、屬性、字段等):一般沒啥用,都是用什么獲取什么
MemberInfo[] members = typeof(MyClass).GetMembers();
foreach (MemberInfo member in members) {Console.WriteLine(member.Name);
}
???????2)獲取某一個類公共構造函數并調用
-
GetConstructors()
獲取所有公共構造函數:
ConstructorInfo[] constructors = typeof(MyClass).GetConstructors();
foreach (ConstructorInfo ctor in constructors) {Console.WriteLine($"參數個數: {ctor.GetParameters().Length}");
}
????????調用構造函數
? ? ? ? 既然獲取到了這一個類中的構造函數,那我們肯定是要來使用的,咋個使用嘞。具體使用有無參構造函數請和有參構造函數的使用,請看下面:
①無參構造函數的調用
步驟:
獲取類型的?
Type
?對象。獲取無參構造函數:通過?
GetConstructor
?方法傳入空參數類型數組。調用構造函數:使用?
ConstructorInfo.Invoke
?方法,參數為?null
。
請看代碼:
using System;
using System.Reflection;public class MyClass {public MyClass() {Console.WriteLine("無參構造函數被調用!");}
}// 獲取 Type 對象
Type type = typeof(MyClass);// 獲取無參構造函數(參數類型數組為空)
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);// 調用構造函數創建實例
object instance = ctor.Invoke(null); // 輸出:無參構造函數被調用!
② 有參構造函數的調用
步驟:
獲取類型的?
Type
?對象。獲取有參構造函數:通過?
GetConstructor
?方法傳入參數類型的?Type
?數組。調用構造函數:使用?
ConstructorInfo.Invoke
?方法,傳入實際參數值數組。
using System;
using System.Reflection;public class Person {public string Name { get; }public int Age { get; }public Person(string name, int age) {Name = name;Age = age;Console.WriteLine($"有參構造函數被調用!Name={Name}, Age={Age}");}
}// 獲取 Type 對象
Type type = typeof(Person);// 定義參數類型數組(string 和 int)
Type[] paramTypes = new Type[] { typeof(string), typeof(int) };// 獲取匹配的構造函數
ConstructorInfo ctor = type.GetConstructor(paramTypes);// 準備實際參數值
object[] parameters = new object[] { "Alice", 30 };// 調用構造函數創建實例
object instance = ctor.Invoke(parameters); // 輸出:有參構造函數被調用!Name=Alice, Age=30
③ 處理多個構造函數的情況
????????如果類型有多個構造函數(例如無參、有參),可以通過?GetConstructors
?遍歷所有構造函數,并選擇需要的簽名。
public class Calculator {public Calculator() {Console.WriteLine("無參構造函數");}public Calculator(int initialValue) {Console.WriteLine($"有參構造函數,初始值={initialValue}");}
}// 獲取所有公共構造函數
ConstructorInfo[] ctors = typeof(Calculator).GetConstructors();foreach (ConstructorInfo ctor in ctors) {ParameterInfo[] parameters = ctor.GetParameters();Console.WriteLine($"構造函數參數個數: {parameters.Length}");
}// 選擇有參構造函數
ConstructorInfo intCtor = typeof(Calculator).GetConstructor(new Type[] { typeof(int) });
object calc = intCtor.Invoke(new object[] { 100 }); // 輸出:有參構造函數,初始值=100
????????注意哈:這里我們都是使用的object萬物之父進行裝載的,你在實際使用時候要轉回去對應的類型的。 還有一個值得注意的是,這里的類我們都是直接給出來了,一般情況是我們是要獲取到別的程序集中的類的,這里只是先講解Type里面的內容,后面我們再講解從別的程序集中獲取對應類信息的。
? ? ? ? 嘿!?寫完了這么多代碼,才將獲取到的類對象實例化出來了,是不是有點心累啊,別急,我們后面會學習更加高效的方式進行類對象的實例化。
小小結:
關鍵點總結
獲取構造函數:
無參構造:GetConstructor(Type.EmptyTypes)
。有參構造:GetConstructor(new Type[] { typeof(string), ... })
。- 調用構造函數:
ConstructorInfo.Invoke(object[] parameters)
。參數必須與構造函數簽名嚴格匹配,否則拋出?TargetParameterCountException
。- 性能優化:緩存?
ConstructorInfo
?對象避免重復反射。
3)獲取某一個類公共成員變量
-
GetFields()
獲取所有公共字段:
FieldInfo[] fields = typeof(MyClass).GetFields();
foreach (FieldInfo field in fields) {Console.WriteLine(field.Name);
}
?獲取指定名稱的公共字段
??Type
?類提供了?GetField(string name)
?方法,用于根據字段名稱獲取單個公共字段的?FieldInfo
?對象。若字段不存在,返回?null
。
FieldInfo field = typeof(MyClass).GetField("FieldName");
?例如:
public class MyClass {public int PublicField = 100;private string _privateField = "Secret";
}// 獲取公共字段
FieldInfo publicField = typeof(MyClass).GetField("PublicField");
Console.WriteLine(publicField?.Name); // 輸出:PublicField// 獲取不存在的字段(返回 null)
FieldInfo invalidField = typeof(MyClass).GetField("NonExistentField");
Console.WriteLine(invalidField == null); // 輸出:True
獲取和設置指定字段的值:
?定義一個學生類:
public class Student {public string Name;public int Age;
}
對其內容進行修改:
using System;
using System.Reflection;// 創建實例
Student student = new Student();// 獲取 Type 對象
Type type = typeof(Student);// 獲取字段并賦值
FieldInfo nameField = type.GetField("Name");
nameField.SetValue(student, "Alice"); // 設置 Name 字段FieldInfo ageField = type.GetField("Age");
ageField.SetValue(student, 20); // 設置 Age 字段// 讀取字段值
Console.WriteLine($"Name: {nameField.GetValue(student)}, Age: {ageField.GetValue(student)}");
????????請注意,我們這里只是為了方便講解的在同一個數據集中的類進行代碼示例的,實際上我們可能不在同一個數據集的。?
4)獲取某一個類公共方法
-
GetMethods()
獲取所有公共方法:?MethodInfo 是方法的反射信息
MethodInfo[] methods = typeof(MyClass).GetMethods();
foreach (MethodInfo method in methods) {Console.WriteLine($"方法名: {method.Name}, 返回值類型: {method.ReturnType}");
}
實戰!
定義一個測試的類:
public class Calculator {// 實例方法public int Add(int a, int b) {return a + b;}// 重載方法(參數類型不同)public double Add(double a, double b) {return a + b;}// 靜態方法public static void PrintVersion() {Console.WriteLine("Calculator v1.0");}
}
?調用公共方法
①基本調用:實例方法
// 創建實例
Calculator calc = new Calculator();// 獲取 Type 對象
Type type = typeof(Calculator);// 獲取方法(無重載時直接按名稱獲取)
MethodInfo addMethod = type.GetMethod("Add", new Type[] { typeof(int), typeof(int) });// 調用方法(需傳遞實例和參數)
object result = addMethod.Invoke(calc, new object[] { 3, 5 });
Console.WriteLine(result); // 輸出:8
②處理重載方法:就是什么類型傳什么參數
// 獲取特定重載方法(通過參數類型區分)
MethodInfo doubleAddMethod = type.GetMethod("Add", new Type[] { typeof(double), typeof(double) });// 調用重載方法
object doubleResult = doubleAddMethod.Invoke(calc, new object[] { 2.5, 3.7 });
Console.WriteLine(doubleResult); // 輸出:6.2
?③調用靜態方法
// 獲取靜態方法(無需實例)
MethodInfo staticMethod = type.GetMethod("PrintVersion", BindingFlags.Public | BindingFlags.Static);// 調用靜態方法(第一個參數傳 null)
staticMethod.Invoke(null, null); // 輸出:Calculator v1.0
關于BindingFlags的解釋:
默認情況下,反射方法(如?GetMethod
)不會自動包含所有成員。例如:
GetMethod("MethodName")
?默認僅搜索公共實例方法。若要搜索靜態方法或私有方法,必須顯式指定?
BindingFlags
。
BindingFlags這是個什么東西呢?
? ? BindingFlags
?是 .NET 中的一個枚舉類型,用于指定反射操作時的搜索條件。通過組合不同的標志,可以精確控制反射的行為,例如:
是否包含公共(
Public
)或非公共(NonPublic
)成員。是否搜索實例(
Instance
)或靜態(Static
)成員。
常用標志:
標志 | 說明 |
---|---|
Public | 包含公共成員(如?public ?方法、屬性)。 |
NonPublic | 包含非公共成員(如?private 、protected ?方法)。 |
Instance | 包含實例成員(非靜態成員)。 |
Static | 包含靜態成員(如?static ?方法)。 |
DeclaredOnly | 僅搜索當前類中定義的成員(不包含繼承的成員)。 |
所以上面出現的:
BindingFlags.Public | BindingFlags.Static
解釋如下:
? |
?是?按位或運算符,用于將多個枚舉標志合并為一個復合值。每個?BindingFlags
?值對應一個二進制位,組合后可以同時滿足多個條件。?
完整示例:
using System;
using System.Reflection;public class Program {public static void Main() {Calculator calc = new Calculator();Type type = typeof(Calculator);// 1. 調用實例方法MethodInfo intAdd = type.GetMethod("Add", new[] { typeof(int), typeof(int) });int sum = (int)intAdd.Invoke(calc, new object[] { 3, 5 });Console.WriteLine($"3 + 5 = {sum}"); // 輸出:3 + 5 = 8// 2. 調用重載方法MethodInfo doubleAdd = type.GetMethod("Add", new[] { typeof(double), typeof(double) });double doubleSum = (double)doubleAdd.Invoke(calc, new object[] { 2.5, 3.7 });Console.WriteLine($"2.5 + 3.7 = {doubleSum}"); // 輸出:2.5 + 3.7 = 6.2// 3. 調用靜態方法MethodInfo staticMethod = type.GetMethod("PrintVersion", BindingFlags.Public | BindingFlags.Static);staticMethod.Invoke(null, null); // 輸出:Calculator v1.0}
}
其實下面的內容就不是特別的重要了,來慢慢看?
5)枚舉操作
-
GetEnumNames()
獲取枚舉的所有名稱:
string[] names = typeof(MyEnum).GetEnumNames(); // MyEnum 是枚舉類型
foreach (string name in names) {Console.WriteLine(name);
}
-
GetEnumValues()
獲取枚舉的所有值:
Array values = typeof(MyEnum).GetEnumValues();
foreach (object value in values) {Console.WriteLine(value);
}
實際使用:
using System;public enum WeekDays { Monday, Tuesday, Wednesday }public class Program {public static void Main() {Type enumType = typeof(WeekDays);// 獲取所有枚舉名稱string[] names = enumType.GetEnumNames();Console.WriteLine("枚舉名稱:");foreach (string name in names) {Console.WriteLine(name); // 輸出:Monday, Tuesday, Wednesday}// 獲取所有枚舉值Array values = enumType.GetEnumValues();Console.WriteLine("枚舉值:");foreach (var value in values) {Console.WriteLine(value); // 輸出:0, 1, 2}}
}
?????????其實我感覺這個作用是在本地獲取到其他數據集里面的枚舉名字,然后調用他那邊提供的方法,在我們這個項目集使用別的枚舉,感覺沒什么意義。除非你這個枚舉兩邊都要有交互,那就代碼復雜度提高了。
6)事件操作
-
GetEvents()
獲取所有公共事件:
EventInfo[] events = typeof(MyClass).GetEvents();
foreach (EventInfo eventInfo in events) {Console.WriteLine(eventInfo.Name);
}
-
GetEvent()
獲取指定名稱的事件:
EventInfo clickEvent = typeof(Button).GetEvent("Click");
示例:
using System;
using System.Reflection;public class Button {public event EventHandler Click;public void TriggerClick() {Click?.Invoke(this, EventArgs.Empty);}
}public class Program {public static void Main() {Type type = typeof(Button);// 獲取所有公共事件EventInfo[] events = type.GetEvents();Console.WriteLine("所有事件:");foreach (EventInfo e in events) {Console.WriteLine(e.Name); // 輸出:Click}// 獲取指定名稱的事件EventInfo clickEvent = type.GetEvent("Click");Console.WriteLine($"找到事件: {clickEvent?.Name}"); // 輸出:Click}
}
7)屬性操作
-
GetProperties()
獲取所有公共屬性:
PropertyInfo[] properties = typeof(MyClass).GetProperties();
foreach (PropertyInfo prop in properties) {Console.WriteLine($"屬性名: {prop.Name}, 類型: {prop.PropertyType}");
}
-
GetProperty()
獲取指定名稱的屬性:
PropertyInfo nameProp = typeof(Person).GetProperty("Name");
示例:?
using System;
using System.Reflection;public class Person {public string Name { get; set; }public int Age { get; set; }
}public class Program {public static void Main() {Type type = typeof(Person);// 獲取所有公共屬性PropertyInfo[] properties = type.GetProperties();Console.WriteLine("所有屬性:");foreach (PropertyInfo prop in properties) {Console.WriteLine(prop.Name); // 輸出:Name, Age}// 獲取指定名稱的屬性PropertyInfo nameProp = type.GetProperty("Name");Console.WriteLine($"找到屬性: {nameProp?.Name}"); // 輸出:Name}
}
8)接口操作
-
GetInterfaces()
獲取類型實現的所有接口:
Type[] interfaces = typeof(MyClass).GetInterfaces();
foreach (Type interfaceType in interfaces) {Console.WriteLine(interfaceType.FullName);
}
-
GetInterface()
獲取指定名稱的接口:
Type iDisposable = typeof(MyClass).GetInterface("IDisposable");
示例:
// 定義接口
public interface IDisplayer {void Display(string message);
}// 實現接口的類
public class Screen : IDisplayer {public void Display(string message) {Console.WriteLine($"屏幕顯示:{message}");}
}
using System;
using System.Reflection;public class Program {public static void Main() {// 獲取 Screen 類的 Type 對象Type screenType = typeof(Screen);// 檢查 Screen 是否實現了 IDisplayer 接口Type displayerInterface = screenType.GetInterface("IDisplayer");if (displayerInterface != null) {Console.WriteLine("Screen 實現了 IDisplayer 接口");}// 創建 Screen 實例object screenInstance = Activator.CreateInstance(screenType);// 調用接口方法// 需要從接口獲取方法,而不是從實現類MethodInfo displayMethod = displayerInterface.GetMethod("Display");displayMethod.Invoke(screenInstance, new object[] { "Hello, Reflection!" });}
}
2.?Assembly
?類
??Assembly
?類表示程序集,用于動態加載和分析程序集內容。
程序集類
主要用來加載其他程序集 加載后
才能用Type來使用其他程序集中的信息
如果使用的不是自己程序集中的內容 需要先加載程序集
比如dll文件(庫文件)
簡單的把庫文件看成一種代碼倉庫 他提供給使用者一些可以直接拿來用的變量,函數或類
三種加載程序集的函數
一般用來加載在同一文件下的其他程序集
Assembly assembly = Assembly.Load("程序集名字");一般用來加載不在同一文件下的其他程序集
Assembly assembly = Assembly.LoadFile("要加載的文件的完全限定路徑");
Assembly assembly = Assembly.LoadFrom("包含程序集清單的文件的名稱或路徑 ");
(1)加載程序集的方式
? ? ?Assembly.Load("程序集名稱"):
加載?當前應用程序域已引用?或?位于應用程序基目錄(如?bin
)?的程序集。適用于加載已知程序集名稱的依賴項。
例如:
using System.Reflection;// 加載當前項目引用的 Newtonsoft.Json 程序集
try {Assembly assembly = Assembly.Load("Newtonsoft.Json");Console.WriteLine($"加載成功: {assembly.FullName}");
} catch (FileNotFoundException ex) {Console.WriteLine($"加載失敗: {ex.Message}");
}
? Assembly.LoadFile("完整路徑")
?:通過?完整文件路徑?加載程序集,不處理依賴項,適用于加載獨立 DLL。
例如:
using System.Reflection;// 加載 D:\Libs\MyLibrary.dll
string path = @"D:\Libs\MyLibrary.dll";
try {Assembly assembly = Assembly.LoadFile(path);Console.WriteLine($"加載成功: {assembly.FullName}");
} catch (Exception ex) {Console.WriteLine($"加載失敗: {ex.Message}");
}
注意:
-
必須提供完整物理路徑(如?
@"C:\Folder\File.dll"
)。 -
多次調用同一路徑會重復加載程序集(占用內存)。
-
不解析依賴項,需手動加載依賴的 DLL。
Assembly.LoadFrom("文件路徑"):
通過?文件路徑或 UNC 路徑?加載程序集,自動處理依賴項,適用于插件系統。
using System.Reflection;// 加載插件程序集(自動處理其依賴項)
string pluginPath = @"D:\Plugins\MyPlugin.dll";
try {Assembly assembly = Assembly.LoadFrom(pluginPath);Console.WriteLine($"加載成功: {assembly.FullName}");
} catch (Exception ex) {Console.WriteLine($"加載失敗: {ex.Message}");
}
對比總結
維度 | Assembly.LoadFrom | Assembly.LoadFile |
---|---|---|
加載上下文 | 獨立的 LoadFrom 上下文 | 無關聯上下文 |
依賴項處理 | 自動解析依賴項 | 需手動加載依賴項 |
路徑格式 | 支持相對路徑、UNC 路徑 | 必須為完整物理路徑 |
重復加載 | 同一路徑的程序集只加載一次 | 同一路徑的程序集會重復加載 |
適用場景 | 插件系統(需處理依賴項) | 獨立工具模塊(無依賴或手動管理依賴) |
?簡單來說,就是LoadFrom這個路徑可以不傳完全,可以只傳相對路徑,就是只傳部分路徑就可以
下面都是一些不太重要的內容,了解有這么個東西就行。?
(2)獲取程序集中的類型
GetTypes()
獲取程序集中定義的所有類型:
Type[] types = assembly.GetTypes();
foreach (Type type in types) {Console.WriteLine(type.FullName);
}
GetExportedTypes()
僅獲取公共可見的類型:
Type[] publicTypes = assembly.GetExportedTypes();
(3)訪問清單信息
-
GetName()
獲取程序集名稱和版本:
AssemblyName name = assembly.GetName();
Console.WriteLine($"名稱: {name.Name}, 版本: {name.Version}");
-
GetReferencedAssemblies()
獲取程序集引用的其他程序集:
AssemblyName[] references = assembly.GetReferencedAssemblies();
foreach (AssemblyName refName in references) {Console.WriteLine(refName.FullName);
}
3.?Activator
?類
? ? ?Activator
?類用于動態創建對象實例。上面我們在Type中不是有一個獲取對象示例嗎,那個有點麻煩,現在我們來快速的進行實例化對象。
用于快速實例化對象的類
用于將Type對象快捷實例化為對象
先得到Type對象
然后 快速實例化一個對象
(1)創建無參實例
????????通過反射調用類型的?無參構造函數?創建實例,適用于簡單對象的動態初始化。
語法:
object instance = Activator.CreateInstance(Type type);
public class MyClass {public MyClass() {Console.WriteLine("無參構造函數被調用!");}
}// 動態創建實例
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type); // 輸出:無參構造函數被調用!
注意事項
若類型沒有無參構造函數,會拋出?
MissingMethodException
。可先檢查是否存在無參構造函數:
bool hasParameterlessCtor = type.GetConstructor(Type.EmptyTypes) != null;
是不是好方便,少寫好幾句代碼!?
和剛才的對比一下。 是不是非常的簡結!
(2)創建帶參實例
????????類型具有帶參數的公共構造函數。
語法:
object instance = Activator.CreateInstance(
? ? Type type,?
? ? params object[] args ?// 參數必須與構造函數簽名嚴格匹配
);
示例:
public class Person {public string Name { get; }public int Age { get; }public Person(string name, int age) {Name = name;Age = age;Console.WriteLine($"帶參構造函數被調用!Name={name}, Age={age}");}
}// 動態創建帶參實例
Type type = typeof(Person);
object[] args = new object[] { "Alice", 30 };
object instance = Activator.CreateInstance(type, args); // 輸出:帶參構造函數被調用!Name=Alice, Age=30
注意事項
若參數類型或數量不匹配,會拋出?
MissingMethodException
?或?ArgumentException
。可通過反射精確獲取構造函數:
ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string), typeof(int) });
object instance = ctor.Invoke(args);
(3)處理私有構造函數
????????類型具有私有構造函數(如單例模式、工廠類)。比較雞肋的操作,它都私有了,還訪問它干嘛。
語法:
// 通過反射獲取私有構造函數
ConstructorInfo ctor = type.GetConstructor(
? ? BindingFlags.NonPublic | BindingFlags.Instance,?
? ? null,?
? ? Type.EmptyTypes, ?// 參數類型數組(無參則為空)
? ? null
);// 調用私有構造函數
object instance = ctor.Invoke(null);
示例:
public class Singleton {private static Singleton _instance;// 私有構造函數private Singleton() {Console.WriteLine("私有構造函數被調用!");}public static Singleton Instance => _instance ??= new Singleton();
}// 強制通過反射調用私有構造函數
Type type = typeof(Singleton);
ConstructorInfo privateCtor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null
);object instance = privateCtor.Invoke(null); // 輸出:私有構造函數被調用!
實際操練:
????????注意你必須將第一個文件的執行文件,可能是exe可能是dll,不管是什么都要放在ReflectionDemoApp文件夾中的bin下才能正常加載數據集
測試用的數據集:
using System;namespace MyReflectionLib {internal class Program {public static void Main(string[] args) {}}public interface IDisplayable {void Display(string msg);int GetCode();}public class DemoClass : IDisplayable {public string PublicField = "初始值";private string _privateField = "私有內容";public string Name { get; set; }// 構造函數重載public DemoClass() => Console.WriteLine("無參構造執行");public DemoClass(string name) => Name = name;// 方法重載public void ShowMessage(string msg) => Console.WriteLine($"字符串消息: {msg}");public void ShowMessage(int code) => Console.WriteLine($"數字代碼: {code}");public string CombineMessages(string a, string b) => a + " + " + b;// 接口實現public void Display(string msg) => Console.WriteLine($"顯示消息: {msg}");public int GetCode() => new Random().Next(1000);// 特殊方法public override string ToString() => $"DemoClass實例[{Name}]";}
}
訪問用的代碼:
using System;
using System.Linq;
using System.Reflection;namespace ReflectionDemoApp {class Program {static void Main(string[] args) {Console.ForegroundColor = ConsoleColor.Cyan;Console.WriteLine("=============== 程序集加載 ===============");Console.ResetColor();// 動態加載程序集Assembly assembly = LoadAssembly("MyReflectionLib.dll");if (assembly == null) return;// 獲取目標類型Type demoType = assembly.GetType("MyReflectionLib.DemoClass");Console.WriteLine($"\n加載類型: {demoType.FullName}");/******************** 構造函數操作 ********************/PrintSectionHeader("構造函數演示", ConsoleColor.Yellow);// 1. 無參構造函數var obj1 = CreateInstance(demoType, "無參實例");// 2. 帶參構造函數var obj2 = CreateInstance(demoType, "帶參實例", new object[] { "Alice" });/******************** 字段和屬性操作 ********************/PrintSectionHeader("字段和屬性操作", ConsoleColor.Green);// 修改公共字段ModifyPublicField(obj1, "PublicField", "新公共字段值");// 修改屬性ModifyProperty(obj2, "Name", "Bob");// 訪問私有字段AccessPrivateField(obj1, "_privateField");/******************** 方法調用操作 ********************/PrintSectionHeader("方法調用演示", ConsoleColor.Magenta);// 調用所有公共方法InvokeAllPublicMethods(obj1, new object[] { "測試消息", 123 });/******************** 枚舉操作 ********************/PrintSectionHeader("枚舉操作演示", ConsoleColor.Blue);ShowEnumValues(assembly, "MyReflectionLib.Status");/******************** 接口操作 ********************/PrintSectionHeader("接口操作演示", ConsoleColor.Cyan);InvokeInterfaceMethod(obj1, "MyReflectionLib.IDisplayable", "Display", "接口調用測試");Console.ReadKey();}#region Helper Methods// ---------------------- 程序集加載 ----------------------static Assembly LoadAssembly(string path) {try {var assembly = Assembly.LoadFrom(path);Console.ForegroundColor = ConsoleColor.Green;Console.WriteLine($"成功加載程序集: {System.IO.Path.GetFileName(path)}");Console.ResetColor();return assembly;}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"加載失敗: {ex.Message}");Console.ResetColor();return null;}}// ---------------------- 實例化對象 ----------------------static object CreateInstance(Type type, string description, object[] args = null) {try {Console.ForegroundColor = ConsoleColor.White;var instance = args == null ?Activator.CreateInstance(type) :Activator.CreateInstance(type, args);Console.WriteLine($"? {description}創建成功");if (args != null) Console.WriteLine($" 參數: [{string.Join(", ", args)}]");return instance;}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"創建失敗: {ex.Message}");return null;}finally {Console.ResetColor();}}// ---------------------- 修改公共字段 ----------------------static void ModifyPublicField(object obj, string fieldName, object value) {try {var field = obj.GetType().GetField(fieldName);field.SetValue(obj, value);Console.ForegroundColor = ConsoleColor.Green;Console.WriteLine($"字段 [{fieldName}] 修改成功 → 新值: {field.GetValue(obj)}");}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"修改字段失敗: {ex.Message}");}finally {Console.ResetColor();}}// ---------------------- 修改屬性 ----------------------static void ModifyProperty(object obj, string propertyName, object value) {try {var prop = obj.GetType().GetProperty(propertyName);if (prop != null && prop.CanWrite) {prop.SetValue(obj, value);Console.ForegroundColor = ConsoleColor.Green;Console.WriteLine($"屬性 [{propertyName}] 修改成功 → 新值: {prop.GetValue(obj)}");} else {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"屬性 [{propertyName}] 不存在或不可寫!");}}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"修改屬性失敗: {ex.Message}");}finally {Console.ResetColor();}}// ---------------------- 訪問私有字段 ----------------------static void AccessPrivateField(object obj, string fieldName) {try {var field = obj.GetType().GetField(fieldName,BindingFlags.NonPublic | BindingFlags.Instance);if (field != null) {Console.ForegroundColor = ConsoleColor.DarkYellow;Console.WriteLine($"私有字段 [{fieldName}] 值: {field.GetValue(obj)}");} else {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"私有字段 [{fieldName}] 不存在!");}}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"訪問私有字段失敗: {ex.Message}");}finally {Console.ResetColor();}}// ---------------------- 調用所有公共方法 ----------------------static void InvokeAllPublicMethods(object obj, params object[] parameters) {var methods = obj.GetType().GetMethods();Console.WriteLine($"\n共發現 {methods.Length} 個公共方法:");foreach (var method in methods) {if (method.IsSpecialName) continue; // 跳過屬性/事件方法Console.ForegroundColor = ConsoleColor.DarkCyan;Console.Write($"\n? 嘗試調用: {method.Name}");Console.ResetColor();Console.Write($" ({string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name))})");try {object result = method.Invoke(obj, MatchParameters(method, parameters));Console.ForegroundColor = ConsoleColor.Green;Console.WriteLine($" ? 調用成功");if (result != null) Console.WriteLine($" 返回值: {result}");}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($" ? 調用失敗: {ex.InnerException?.Message ?? ex.Message}");}finally {Console.ResetColor();}}}// ---------------------- 智能參數匹配 ----------------------static object[] MatchParameters(MethodInfo method, object[] parameters) {return parameters.Take(method.GetParameters().Length).ToArray();}// ---------------------- 枚舉操作 ----------------------static void ShowEnumValues(Assembly assembly, string enumTypeName) {try {Type enumType = assembly.GetType(enumTypeName);if (enumType == null || !enumType.IsEnum) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"枚舉類型 [{enumTypeName}] 不存在或不是枚舉類型!");return;}Console.ForegroundColor = ConsoleColor.Blue;Console.WriteLine($"\n枚舉 [{enumType.Name}] 所有值:");Array values = Enum.GetValues(enumType);foreach (var value in values) {Console.WriteLine($" {value} = {(int)value}");}}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"枚舉操作失敗: {ex.Message}");}finally {Console.ResetColor();}}// ---------------------- 接口方法調用 ----------------------static void InvokeInterfaceMethod(object obj, string interfaceName, string methodName, params object[] args) {try {Type interfaceType = obj.GetType().GetInterface(interfaceName);if (interfaceType == null) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"對象未實現接口 [{interfaceName}]!");return;}MethodInfo method = interfaceType.GetMethod(methodName);if (method == null) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"接口 [{interfaceName}] 中不存在方法 [{methodName}]!");return;}Console.ForegroundColor = ConsoleColor.Cyan;Console.WriteLine($"\n通過接口 [{interfaceType.Name}] 調用方法 [{methodName}]");object result = method.Invoke(obj, args);Console.ForegroundColor = ConsoleColor.Green;Console.WriteLine("? 接口方法調用成功");if (result != null) Console.WriteLine($" 返回值: {result}");}catch (Exception ex) {Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine($"接口方法調用失敗: {ex.InnerException?.Message ?? ex.Message}");}finally {Console.ResetColor();}}// ---------------------- 模塊標題打印 ----------------------static void PrintSectionHeader(string text, ConsoleColor color) {Console.WriteLine();Console.ForegroundColor = color;Console.WriteLine($"══════════ {text} ══════════");Console.ResetColor();}#endregion}
}
結果:
?
四、總結
反射核心類總結表
類名 | API / 屬性 | 描述 | 示例 |
---|---|---|---|
Type | typeof(MyClass) | 獲取類型的?Type ?對象(編譯時已知類型)。 | Type type = typeof(MyClass); |
obj.GetType() | 通過實例獲取?Type ?對象。 | Type type = new MyClass().GetType(); | |
Type.GetType("Namespace.MyClass") | 通過類型全名獲取?Type ?對象(需包含程序集信息)。 | Type type = Type.GetType("MyReflectionLib.DemoClass, MyReflectionLib"); | |
type.Assembly | 獲取類型所屬的程序集。 | Assembly assembly = type.Assembly; | |
type.GetMethods() | 獲取所有公共方法。 | MethodInfo[] methods = type.GetMethods(); | |
type.GetMethod("MethodName", Type[] paramTypes) | 獲取指定名稱和參數類型的公共方法。 | MethodInfo method = type.GetMethod("Add", new[] { typeof(int) }); | |
type.GetProperties() | 獲取所有公共屬性。 | PropertyInfo[] props = type.GetProperties(); | |
type.GetProperty("PropertyName") | 獲取指定名稱的公共屬性。 | PropertyInfo prop = type.GetProperty("Name"); | |
type.GetFields() | 獲取所有公共字段。 | FieldInfo[] fields = type.GetFields(); | |
type.GetField("FieldName", BindingFlags) | 獲取指定名稱的字段(可指定?NonPublic ?訪問私有字段)。 | FieldInfo field = type.GetField("_private", BindingFlags.NonPublic); | |
type.GetConstructors() | 獲取所有公共構造函數。 | ConstructorInfo[] ctors = type.GetConstructors(); | |
type.GetConstructor(Type[] paramTypes) | 獲取匹配參數類型的構造函數。 | ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) }); | |
type.GetInterfaces() | 獲取類型實現的所有接口。 | Type[] interfaces = type.GetInterfaces(); | |
type.GetInterface("InterfaceName") | 獲取指定名稱的接口。 | Type iface = type.GetInterface("IDisplayable"); | |
type.GetEnumNames() | 獲取枚舉類型的所有名稱(僅適用于枚舉)。 | string[] names = typeof(Status).GetEnumNames(); | |
type.GetEvents() | 獲取所有公共事件。 | EventInfo[] events = type.GetEvents(); | |
Assembly | Assembly.Load("AssemblyName") | 加載當前應用程序域已知的程序集(基于名稱)。 | Assembly assembly = Assembly.Load("MyReflectionLib"); |
Assembly.LoadFrom("FilePath") | 通過文件路徑加載程序集(自動處理依賴項)。 | Assembly assembly = Assembly.LoadFrom("Plugins/MyPlugin.dll"); | |
Assembly.LoadFile("FullPath") | 通過完整路徑加載程序集(不處理依賴項)。 | Assembly assembly = Assembly.LoadFile(@"C:\Lib\MyLib.dll"); | |
assembly.GetTypes() | 獲取程序集中定義的所有類型。 | Type[] types = assembly.GetTypes(); | |
assembly.GetExportedTypes() | 獲取程序集中所有公共可見的類型。 | Type[] publicTypes = assembly.GetExportedTypes(); | |
assembly.GetName() | 獲取程序集名稱和版本信息。 | AssemblyName name = assembly.GetName(); | |
assembly.GetReferencedAssemblies() | 獲取程序集引用的其他程序集。 | AssemblyName[] refs = assembly.GetReferencedAssemblies(); | |
Activator | Activator.CreateInstance(Type) | 創建無參構造函數的實例。 | object obj = Activator.CreateInstance(typeof(MyClass)); |
Activator.CreateInstance(Type, object[] args) | 創建帶參構造函數的實例。 | object obj = Activator.CreateInstance(type, new[] { "Alice" }); | |
Activator.CreateInstance(Type, BindingFlags, ...) | 創建實例(可指定?NonPublic ?訪問私有構造函數)。 | object obj = Activator.CreateInstance(type, nonPublic: true); |
?