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

C# 反射效能基礎測試:物件建構 350 倍差距與 Type.GetType 快取影響

     編輯:關於C語言

前言

可配置系統框架大量使用反射,需量化其效能代價。本文呈現最基礎的物件建構反射測試,揭示 350 倍效能差距與 Type.GetType 快取帶來的 20 倍優化。

測試類別


namespace ReflectionTest.Test
{
    public class CTester
    {
        private double _a = 10;
        public CTester() { _a = 10; }
        public void Test1() => _a = (_a - 0.0001) * 1.0001;
        public double GetA() => _a;
    }
}

測試 1:直接 new vs 反射 InvokeMember 建構

直接建構 (基準)


private void TestDirectConstructor()
{
    DateTime now = DateTime.Now;
    for (int i = 0; i < 1000; i++)
        for (int j = 0; j < 100; j++)
            var obj = new CTester();  // 100,000 次
    TimeSpan span = DateTime.Now - now;
    label1.Text = $"Direct: {span}";  // ~16 ms
}

反射建構 (未快取 Type)


private void TestReflectConstructorUncached()
{
    DateTime now = DateTime.Now;
    for (int i = 0; i < 1000; i++)
        for (int j = 0; j < 100; j++)
        {
            // 每次都呼叫 Type.GetType - 極度低效
            Type t = Type.GetType("ReflectionTest.Test.CTester");
            var obj = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
        }
    TimeSpan span = DateTime.Now - now;
    label2.Text = $"Reflect (uncached): {span}";  // ~5,520 ms
}

反射建構 (快取 Type)


private void TestReflectConstructorCached()
{
    Type t = Type.GetType("ReflectionTest.Test.CTester");  // 提前快取
    DateTime now = DateTime.Now;
    for (int i = 0; i < 1000; i++)
        for (int j = 0; j < 100; j++)
            var obj = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
    TimeSpan span = DateTime.Now - now;
    label2.Text = $"Reflect (cached): {span}";  // ~1,332 ms
}

關鍵發現

測試案例耗時vs 直接呼叫說明
直接 new16 ms1x (基準)JIT 最佳化、直接記憶體配置
反射 (未快取 Type)5,520 ms350x每次解析型別字串、查找組件、載入中繼資料
反射 (快取 Type)1,332 ms83x移除 Type.GetType 開銷,剩餘 InvokeMember 解析成本

Type.GetType 內部運作

Type.GetType("Namespace.TypeName, AssemblyName") 執行:

  1. 解析組件名稱、型別名稱
  2. 載入組件 (若未載入)
  3. 在組件中查找型別定義
  4. 建構 RuntimeType 物件

此過程涉及字串解析、鎖競爭、組件載入邏輯、中繼資料查找,極不適合放在迴圈中。

最佳實踐:反射物件快取模式


public static class ReflectionCache
{
    private static readonly Dictionary _typeCache = new();
    private static readonly Dictionary _ctorCache = new();
    private static readonly object _lock = new();
    
    public static Type GetType(string typeName)
    {
        if (_typeCache.TryGetValue(typeName, out var t)) return t;
        lock (_lock)
        {
            if (_typeCache.TryGetValue(typeName, out t)) return t;
            t = Type.GetType(typeName) ?? throw new ArgumentException($"Type not found: {typeName}");
            _typeCache[typeName] = t;
            return t;
        }
    }
    
    public static ConstructorInfo GetConstructor(string typeName)
    {
        string key = $"ctor:{typeName}";
        if (_ctorCache.TryGetValue(key, out var ctor)) return ctor;
        lock (_lock)
        {
            if (_ctorCache.TryGetValue(key, out ctor)) return ctor;
            var type = GetType(typeName);
            ctor = type.GetConstructor(Type.EmptyTypes) ?? throw new ArgumentException($"No parameterless ctor: {typeName}");
            _ctorCache[key] = ctor;
            return ctor;
        }
    }
}

// 使用快取版本
var type = ReflectionCache.GetType("ReflectionTest.Test.CTester");
var ctor = ReflectionCache.GetConstructor("ReflectionTest.Test.CTester");
var obj = ctor.Invoke(null);  // 比 InvokeMember 更快

進一步優化:ConstructorInfo.Invoke vs InvokeMember


// InvokeMember 通用但慢
t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);

// ConstructorInfo.Invoke 專用於建構,較快
ctor.Invoke(null);  // 少了 BindingFlags 解析、成員查找步驟

// 最快:Delegate.CreateDelegate
var factory = (Func)Delegate.CreateDelegate(typeof(Func), ctor);
var obj = factory();  // 接近直接 new 效能

總結

反射建構物件比直接 new 慢 350 倍;快取 Type 可降至 83 倍;改用 ConstructorInfo + Delegate.CreateDelegate 可逼近直接呼叫效能。鐵律:熱點路徑禁用反射;必須用時,快取 MemberInfo、用強型別委派。

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