首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

.NET 反射入門到進階:動態型別發現、建構與調用完整教學

     編輯:關於C語言

什麼是反射?

反射 是 .NET 在執行時檢查、操作型別中繼資料的機制。核心命名空間:System.Reflection。主要類別:

  • Assembly:組件(.dll/.exe),載入、查詢型別、建立實例
  • Type:型別資訊,成員(方法、屬性、欄位、事件、建構函式)
  • MethodInfo / PropertyInfo / FieldInfo / ConstructorInfo:具體成員操作
  • Activator:動態建立實例

命名空間 vs 組件:關鍵區別

概念說明
命名空間邏輯分組(如 System.Collections.Generic),編譯時概念,不影響部署
組件物理部署單位(.dll/.exe),執行時載入單位,包含型別實作

關鍵:一個組件可包含多個命名空間;一個命名空間可跨多個組件。反射查詢型別必須指定組件(mscorlib.dll 除外)。

取得 Type 物件的三種方式

// 1. 從實例取得(最常用)
Type t1 = "hello".GetType();

// 2. 從型別名稱取得(需完整限定名稱 + 組件名)
Type t2 = Type.GetType("System.Data.DataTable, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

// 3. 從組件取得
Assembly asm = Assembly.Load("System.Data");
Type t3 = asm.GetType("System.Data.DataTable");

// 4. 編譯時已知型別用 typeof(效能最佳)
Type t4 = typeof(string);

動態建立實例

// 無參數建構函式
Type t = Type.GetType("System.Data.DataTable, System.Data");
DataTable dt = (DataTable)Activator.CreateInstance(t);

// 有參數建構函式
Type t2 = typeof(TestClass); // 假設 public TestClass(string name)
object[] args = { "Timmy" };
TestClass obj = (TestClass)Activator.CreateInstance(t2, args);

// 泛型版本(.NET Core 3.0+)
T instance = Activator.CreateInstance<T>();

動態調用方法

public class TestClass
{
    public string GetValue(string prefix) => $"{prefix} : {_value}";
    public string Value { get; set; }
    private string _value;
}

// 取得 MethodInfo
MethodInfo method = t.GetMethod("GetValue"); // 公開實例方法
// 或精確繫結
MethodInfo method = t.GetMethod("GetValue", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null);

// 調用
object[] parameters = { "Hello" };
object result = method.Invoke(obj, parameters); // 回傳 object,需轉型
Console.WriteLine(result); // "Hello : Timmy"

屬性與欄位操作

// 設定屬性
PropertyInfo prop = t.GetProperty("Value");
prop.SetValue(obj, "New Value");

// 讀取屬性
string val = (string)prop.GetValue(obj);

// 欄位(含私有)
FieldInfo field = t.GetField("_value", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(obj, "Direct Field Access");

動態建立委派(高效能調用)

MethodInfo.Invoke 有效能開銷。頻繁調用時建立委派:

// 委派型別
delegate string TestDelegate(string value);

// 建立委派(.NET 2.0+)
TestDelegate del = (TestDelegate)Delegate.CreateDelegate(
    typeof(TestDelegate), obj, "GetValue");

// 直接調用(接近直接呼叫效能)
string result = del("Hello");

.NET Core 3.0+ 更推薦 MethodInfo.CreateDelegate<T>():

var del = method.CreateDelegate<Func<string, string>>(obj);
string result = del("Hello");

實戰場景:外掛架構

// 1. 定義介面契約(共用組件)
public interface IPlugin
{
    string Name { get; }
    void Execute();
}

// 2. 主程式動態載入外掛
public class PluginLoader
{
    public IEnumerable<IPlugin> LoadPlugins(string pluginsPath)
    {
        foreach (string dll in Directory.GetFiles(pluginsPath, "*.dll"))
        {
            Assembly asm = Assembly.LoadFrom(dll);
            foreach (Type t in asm.GetTypes())
            {
                if (typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
                {
                    yield return (IPlugin)Activator.CreateInstance(t);
                }
            }
        }
    }
}

// 3. 外掛實作(獨立組件)
public class LoggerPlugin : IPlugin
{
    public string Name => "Logger";
    public void Execute() => Console.WriteLine("Logging...");
}

效能與最佳實踐

操作效能建議
頻繁反射調用快取 Type/MethodInfo、建立委派、使用 Expression 編譯 Lambda
熱路徑避免反射,改用原始碼產生器、IL.Emit、DynamicMethod
AOT/Trimming 相容標註 [DynamicallyAccessedMembers]、保留中繼資料、偏好介面/原始碼產生器
安全性避免 BindingFlags.NonPublic 破壞封裝、沙箱環境限制反射權限

現代替代方案

  • Source Generators:編譯時代碼生成,零反射開銷
  • System.Text.Json 反序列化:內建原始碼生成器,無反射
  • Minimal APIs / 介面:依賴注入容器解析,避免手動反射
  • LibraryImport / NativeAOT:P/Invoke 現代化
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved