狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

.NET Core 反射底層原理淺談

freeflydom
2024年11月16日 8:18 本文熱度 736

前期綁定與后期綁定

在.NET中,前期綁定(Early Binding)是指在編譯時就確定了對象的類型和方法,而后期綁定(Late Binding)或動態綁定是在運行時確定對象的類型和方法。

前置知識:C#類型系統結構

C#作為C++++ ,在類型系統上沿用C++的類型系統

前期綁定

在代碼能執行之前,將代碼中依賴的assembly,module,class,method,field等類型系統的元素提前構建好。
前期綁定的優點是編譯時類型檢查,提高了類型安全性和性能。缺點是如果需要更換類型,需要重新編譯代碼。靈活性不夠

比如一個簡單的的控制臺,就自動提前加載了各種需要的DLL文件。完成前期綁定。

后期綁定

后期綁定的優點是可以在運行時更改類型,無需重新編譯代碼。缺點是在編譯時不進行類型檢查,可能導致運行時錯誤。
幾個常用的場景,比如dynamic ,多態,System.Reflection

舉個例子,使用Reflection下的“元數據查詢API”,動態加載DLL

            var dllpath = "xxx.dll";
            Assembly assembly = Assembly.LoadFrom(dllpath);//構建Assembly+Module
            Type dataAccessType = assembly.GetType("xxxxx");//構建Class(MethodTable+EEClass)
            object dataAccess = Activator.CreateInstance(dataAccessType);//在托管堆中創建MT實例
            MethodInfo addMethod = dataAccessType.GetMethod("Add");//構建MethodDesc
			
            addMethod.Invoke(dataAccess, new object[] { "hello world" });//調用方法

反射

反射的本質就是“操作元數據”

什么是元數據?

MetaData,本是上就是存儲在dll中的一個信息數據庫,記錄了這個assembled中有哪些方法,哪些類,哪些屬性等等信息

可以看到,各種Table組成的信息,是不是類似一個數據庫?

舉個例子:
執行Type.GetType("int"),反射會在MetaData尋找"int"的類型。但在運行時會返回null.因為MetaData中只有"System.Int32"這個字符串。

反射如何查詢MetaData?

通過Reflection XXXInfo系列API 查詢所有細節

Type t = typeof(System.IO.FileStream);
FieldInfo[] fi = t.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
PropertyInfo[] pi = t.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
EventInfo[] ei = t.GetEvents(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
......

反射如何構建類型系統

通過Reflection XXXBuilder系列API 構建一個全新的類型

            AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("MyAssembly"), AssemblyBuilderAccess.RunAndCollect);//創建Assembly
            ModuleBuilder mob = ab.DefineDynamicModule("MyModule");//創建Module
            TypeBuilder tb = mob.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);//創建Class
            MethodBuilder mb = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int), typeof(int) });//創建MethodTable
            ILGenerator il = mb.GetILGenerator();//通過IL API 動態構建MethodDesc
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Ret);
            Type type = tb.CreateType();  //mt + eeclass
            MethodInfo method = type.GetMethod("SumMethod");
            Console.WriteLine(method.Invoke(null, new object[] { 5, 10 }));

反射底層調用

C#的類型系統,與C++的類型系統是一一對應的。因此其底層必定是調用C++的方法。
示意圖如下,有興趣的小伙伴可以去參考coreclr的源碼

眼見為實,以Invoke為例

反射到底慢在哪?

  1. 動態解析
    從上面可知道,反射作為后期綁定,在runtime中要根據metadata查詢出信息,嚴重依賴字符串匹配,這本身就增加了額外的操作
  2. 動態調用
    使用反射調用方法時,先要將參數打包成數組,再解包到線程棧上。又是額外操作。
  3. 無法在編譯時優化
    反射是動態的臨時調用,JIT無法優化。只能根據代碼一步一步執行。
  4. 額外的安全檢查
    反射會涉及到訪問和修改只讀字段等操作,運行時需要進行額外的安全性檢查,這也會增加一定的開銷
  5. 緩存易失效
    反射如果參數發生變化,那么緩存的匯編就會失效。又需要重新查找與解析。

總之,千言萬語匯成一句話。最好的反射就是不要用反射。除非你能保證對性能要求不高/緩存高命中率

CLR的對反射的優化

除了緩存反射的匯編,.NET 中提供了一系列新特性來盡可能的繞開“反射”

Emit

Emit 是 .NET 提供的一種動態生成和編譯代碼的技術。通過 Emit,我們可以動態生成一個新的方法,這個方法可以直接訪問私有成員,這對于一些特殊場景非常有用,比如動態代理、代碼生成器、AOP(面向切面編程)等.

public class Person
{
    private int _age;
    public override string ToString()
    {
        return _age.ToString();
    }
}
static void EmitTest(Person person)
{
    // 獲取Person類的類型對象
    Type personType = typeof(Person);
    // 獲取私有字段_age的FieldInfo,無法避免部分使用反射
    FieldInfo ageField = personType.GetField("_age", BindingFlags.Instance | BindingFlags.NonPublic);
    if (ageField == null)
    {
        throw new ArgumentException("未找到指定的私有字段");
    }
    // 創建一個動態方法
    DynamicMethod dynamicMethod = new DynamicMethod("SetAgeValue", null, new Type[] { typeof(Person), typeof(int) }, personType);
    // 獲取IL生成器
    ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
    // 將傳入的Person對象加載到計算棧上(this指針)
    ilGenerator.Emit(OpCodes.Ldarg_0);
    // 將傳入的新值加載到計算棧上
    ilGenerator.Emit(OpCodes.Ldarg_1);
    // 將新值存儲到對應的私有字段中
    ilGenerator.Emit(OpCodes.Stfld, ageField);
    // 返回(因為方法無返回值,這里只是結束方法執行)
    ilGenerator.Emit(OpCodes.Ret);
    // 創建委托類型,其簽名與動態方法匹配
    Action<Person, int> setAgeAction = (Action<Person, int>)dynamicMethod.CreateDelegate(typeof(Action<Person, int>));
    // 通過委托調用動態生成的方法來修改私有字段的值
    setAgeAction(person, 100);
}

切構建代碼又臭又長。

Expression

Expression 是 .NET 提供的一種表達式樹的技術。通過 Expression,我們可以創建一個表達式樹,然后編譯這個表達式樹,生成一個可以訪問私有成員的方法

static void ExpressionTest(Person person)
{
    // 獲取Person類的類型對象
    Type personType = typeof(Person);
    // 獲取私有字段_age的FieldInfo,無法避免部分使用反射
    FieldInfo ageField = personType.GetField("_age", BindingFlags.Instance | BindingFlags.NonPublic);
    if (ageField == null)
    {
        throw new ArgumentException("未找到指定的私有字段");
    }
    // 創建參數表達式,對應傳入的Person對象實例
    ParameterExpression instanceParam = Expression.Parameter(personType, "instance");
    // 創建參數表達式,對應傳入的新值
    ParameterExpression newValueParam = Expression.Parameter(typeof(int), "newValue");
    // 創建一個賦值表達式,將新值賦給私有字段
    BinaryExpression assignExpression = Expression.Assign(Expression.Field(instanceParam, ageField), newValueParam);
    // 創建一個包含賦值表達式的表達式塊,這里因為只有一個賦值操作,所以塊里就這一個表達式
    BlockExpression blockExpression = Expression.Block(assignExpression);
    // 創建一個可執行的委托,其類型與表達式塊的邏輯匹配
    Action<Person, int> setAgeAction = Expression.Lambda<Action<Person, int>>(blockExpression, instanceParam, newValueParam).Compile();
    // 通過委托調用表達式樹生成的邏輯來修改私有字段的值
    setAgeAction(person, 100);
}

切構建代碼又臭又長。

UnsafeAccessorAttribute

.Net 8中引入了新特性UnsafeAccessorAttribute 。
使用該特性,來提供對私有字段的快速修改

static void New()
{
    var person = new Person();
    GetAgeField(person) = 100;
}
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_age")]
static extern ref int GetAgeField(Person counter);

為什么它這么快?

對于C#來說,私有類型是OOP語言的定義。它定義了什么是私有類型,它的行為是什么。
但對于程序本身來說,代碼和數據都只是一段內存,實際上你的指針想訪問哪就訪問哪。哪管你什么私有類型。換一個指向地址不就得了。因此CLR開放了這么一個口子,利用外部訪問直接操作內存。看它的命名UnsafeAccessor就能猜到意圖了

3,2,1. 上匯編!!!

直接將rax寄存器偏移量+8,直接返回int(占用4字節,偏移量8)類型的_age。 沒有Emit,Expression的彎彎繞繞,絲毫不拖泥帶水。

.NET 9中的改進

支持泛型,更優雅。
https://learn.microsoft.com/zh-cn/dotnet/core/compatibility/core-libraries/9.0/unsafeaccessor-generics

參考資料

https://blog.csdn.net/sD7O95O/article/details/133002995
https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.compilerservices.unsafeaccessorattribute?view=net-8.0

轉自https://www.cnblogs.com/lmy5215006/p/18545334


該文章在 2024/11/16 8:18:06 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved