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

C# 反射效能深度測試:建構子、方法呼叫、靜態方法 350 倍差距分析

     編輯:關於C語言

前言

反射提供強大的動態能力,但效能代價高昂。本文呈實測數據:直接呼叫 vs 反射呼叫在建構子、實例方法、靜態方法三場景的效能差距,並分析 IL 層面根本原因。

測試環境與基準程式碼


// 測試類別
public class CTester
{
    private double _a = 10;
    public void Test1() => _a = (_a - 0.0001) * 1.0001;
    public double GetA() => _a;
}

// 測試參數:1000 外層 × 100 內層 = 100,000 次呼叫
const int OUTER = 1000;
const int INNER = 100;

場景 1:物件建構

直接呼叫


DateTime now = DateTime.Now;
for (int i = 0; i < OUTER; i++)
    for (int j = 0; j < INNER; j++)
        var obj = new CTester();
TimeSpan direct = DateTime.Now - now;  // ~16 ms

反射呼叫 (Type.GetType 在迴圈內)


DateTime now = DateTime.Now;
for (int i = 0; i < OUTER; i++)
    for (int j = 0; j < INNER; j++)
    {
        Type t = Type.GetType("ReflectionTest.Test.CTester");
        var obj = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
    }
TimeSpan reflect = DateTime.Now - now;  // ~5,520 ms (350x)

反射呼叫 (Type.GetType 移至迴圈外)


Type t = Type.GetType("ReflectionTest.Test.CTester");  // 提前取得
DateTime now = DateTime.Now;
for (int i = 0; i < OUTER; i++)
    for (int j = 0; j < INNER; j++)
        var obj = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
TimeSpan reflectOptimized = DateTime.Now - now;  // ~1,332 ms (20x)

場景 2:實例方法呼叫

直接呼叫


var obj = new CTester();
DateTime now = DateTime.Now;
for (int i = 0; i < OUTER; i++)
    for (int j = 0; j < INNER; j++)
        obj.Test1();
TimeSpan direct = DateTime.Now - now;  // ~10 ms

反射呼叫 (InvokeMember)


Type t = Type.GetType("ReflectionTest.Test.CTester");
var obj = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
DateTime now = DateTime.Now;
for (int i = 0; i < OUTER; i++)
    for (int j = 0; j < INNER; j++)
        t.InvokeMember("Test1", BindingFlags.InvokeMethod, null, obj, null);
TimeSpan reflect = DateTime.Now - now;  // ~2,500 ms (250x)

場景 3:靜態方法呼叫


// 直接呼叫 ~5 ms
// 反射呼叫 ~3,164 ms (600x+)

效能比較總表

操作直接呼叫反射 (未優化)反射 (快取 Type)倍數差距 (未優化)倍數差距 (快取)
建構子16 ms5,520 ms1,332 ms350x20x
實例方法10 ms2,500 ms—250x—
靜態方法5 ms3,164 ms—630x—

IL 層面根本原因分析

直接呼叫 IL


// newobj 直接配置記憶體並呼叫建構子
IL_0001: newobj instance void CTester::.call()
// callvirt 直接呼叫實例方法
IL_0007: callvirt instance void CTester::Test1()

反射呼叫 IL


// 複雜的反射呼叫鏈
IL_0001: ldtoken ReflectionTest.Test.CTester
IL_0006: call Type.GetTypeFromHandle
IL_000B: ldstr "Test1"
IL_0010: ldc.i4.s 16 (BindingFlags.InvokeMethod)
IL_0012: ldnull
IL_0013: ldloc.0 (obj)
IL_0014: ldnull
IL_0015: callvirt Type.InvokeMember(string, BindingFlags, Binder, object, object[])

核心差異:callvirt 直接跳轉到方法入口點;InvokeMember 經過完整反射解析流程:

  1. 權限檢查
  2. 成員查找 (名稱、BindingFlags、型別匹配)
  3. 參數驗證與封送
  4. 動態建構呼叫框架
  5. 執行目標方法
  6. 回傳值封箱/解封箱

優化策略與替代方案

策略適用場景效能提升複雜度
快取 Type/MethodInfo/PropertyInfo重複反射同一成員10-20x低
Delegate.CreateDelegate固定目標方法,高頻呼叫接近直接呼叫中
Expression Tree 編譯 Lambda動態建構邏輯,重複執行接近直接呼叫中高
DynamicMethod + ILGenerator極致效能需求接近直接呼叫高
Source Generators (C# 9+)編譯期產生程式碼替代反射零執行期反射中

Delegate.CreateDelegate 實例


// 一次性建立委派
MethodInfo method = typeof(CTester).GetMethod("Test1");
var action = (Action)Delegate.CreateDelegate(typeof(Action), method);

// 高頻呼叫:效能接近直接呼叫
for (int i = 0; i < 100000; i++)
    action(obj);

總結

反射效能成本極高 (20x-600x),核心在於 InvokeMember 的動態解析開銷。熱點路徑嚴禁使用反射。必須使用時:快取 MemberInfo、改用 Delegate.CreateDelegate、或採用原始碼產生/Expression Tree 替代。架構設計階段應評估動態性需求 vs 效能預算,優先選擇強型別、編譯期綁定方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved