欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET Core 反射底層原理解析

 更新時間:2024年11月18日 09:42:18   作者:叫我安不理  
本文介紹了.NET中的反射機制,包括前期綁定(EarlyBinding)和后期綁定(LateBinding)的概念,反射是一種強大的工具,但在性能要求較高的場景中應謹慎使用,感興趣的朋友跟隨小編一起看看吧

簡介

反射,反射,程序員的快樂。

前期綁定與后期綁定

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

前置知識:C#類型系統(tǒng)結構

C#作為C++++ ,在類型系統(tǒng)上沿用C++的類型系統(tǒng)

前期綁定

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

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

后期綁定

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

舉個例子,使用Reflection下的“元數(shù)據(jù)查詢API”,動態(tài)加載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);//在托管堆中創(chuàng)建MT實例
            MethodInfo addMethod = dataAccessType.GetMethod("Add");//構建MethodDesc
            addMethod.Invoke(dataAccess, new object[] { "hello world" });//調(diào)用方法

反射

反射的本質就是“操作元數(shù)據(jù)”

什么是元數(shù)據(jù)?

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

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

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

反射如何查詢MetaData?

通過Reflection XXXInfo系列API 查詢所有細節(jié)

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);
......

反射如何構建類型系統(tǒng)

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

            AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("MyAssembly"), AssemblyBuilderAccess.RunAndCollect);//創(chuàng)建Assembly
            ModuleBuilder mob = ab.DefineDynamicModule("MyModule");//創(chuàng)建Module
            TypeBuilder tb = mob.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);//創(chuàng)建Class
            MethodBuilder mb = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int), typeof(int) });//創(chuàng)建MethodTable
            ILGenerator il = mb.GetILGenerator();//通過IL API 動態(tài)構建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 }));

反射底層調(diào)用

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

眼見為實,以Invoke為例

反射到底慢在哪?

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

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

CLR的對反射的優(yōu)化

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

Emit

Emit 是 .NET 提供的一種動態(tài)生成和編譯代碼的技術。通過 Emit,我們可以動態(tài)生成一個新的方法,這個方法可以直接訪問私有成員,這對于一些特殊場景非常有用,比如動態(tài)代理、代碼生成器、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("未找到指定的私有字段");
    }
    // 創(chuàng)建一個動態(tài)方法
    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);
    // 返回(因為方法無返回值,這里只是結束方法執(zhí)行)
    ilGenerator.Emit(OpCodes.Ret);
    // 創(chuàng)建委托類型,其簽名與動態(tài)方法匹配
    Action<Person, int> setAgeAction = (Action<Person, int>)dynamicMethod.CreateDelegate(typeof(Action<Person, int>));
    // 通過委托調(diào)用動態(tài)生成的方法來修改私有字段的值
    setAgeAction(person, 100);
}

切構建代碼又臭又長。

Expression

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

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("未找到指定的私有字段");
    }
    // 創(chuàng)建參數(shù)表達式,對應傳入的Person對象實例
    ParameterExpression instanceParam = Expression.Parameter(personType, "instance");
    // 創(chuàng)建參數(shù)表達式,對應傳入的新值
    ParameterExpression newValueParam = Expression.Parameter(typeof(int), "newValue");
    // 創(chuàng)建一個賦值表達式,將新值賦給私有字段
    BinaryExpression assignExpression = Expression.Assign(Expression.Field(instanceParam, ageField), newValueParam);
    // 創(chuàng)建一個包含賦值表達式的表達式塊,這里因為只有一個賦值操作,所以塊里就這一個表達式
    BlockExpression blockExpression = Expression.Block(assignExpression);
    // 創(chuàng)建一個可執(zhí)行的委托,其類型與表達式塊的邏輯匹配
    Action<Person, int> setAgeAction = Expression.Lambda<Action<Person, int>>(blockExpression, instanceParam, newValueParam).Compile();
    // 通過委托調(diào)用表達式樹生成的邏輯來修改私有字段的值
    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語言的定義。它定義了什么是私有類型,它的行為是什么。
但對于程序本身來說,代碼和數(shù)據(jù)都只是一段內(nèi)存,實際上你的指針想訪問哪就訪問哪。哪管你什么私有類型。換一個指向地址不就得了。因此CLR開放了這么一個口子,利用外部訪問直接操作內(nèi)存??此拿鸘nsafeAccessor就能猜到意圖了

3,2,1. 上匯編?。。?/p>

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

.NET 9中的改進

支持泛型,更優(yōu)雅。
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

到此這篇關于.NET Core 反射底層原理淺談的文章就介紹到這了,更多相關.NET Core 反射底層原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C#開發(fā)微信 二維碼鼠標滑動 圖像顯示隱藏效果(推薦)

    C#開發(fā)微信 二維碼鼠標滑動 圖像顯示隱藏效果(推薦)

    客戶端微信在二維碼狀態(tài)下,鼠標滑過,會有一張手機的圖片滑動滑出,從隱藏到顯示,從顯示到隱藏。效果非常棒,本文思路介紹明確,感興趣的朋友一起看看吧
    2016-11-11
  • ASP.NET對txt文件相關操作(讀取、寫入、保存)

    ASP.NET對txt文件相關操作(讀取、寫入、保存)

    這篇文章主要介紹了ASP.NETtxt文件相關操作,包括讀取、寫入、保存,需要的朋友可以參考下
    2015-09-09
  • asp.net?core集成ElasticSearch實現(xiàn)全文檢索功能

    asp.net?core集成ElasticSearch實現(xiàn)全文檢索功能

    索引是Elasticsearch中用于存儲文檔的容器,你可以使用Elasticsearch的REST?API、官方客戶端庫(如NEST)或Kibana等工具來創(chuàng)建和管理索引,本文給大家介紹asp.net?core集成ElasticSearch實現(xiàn)全文檢索功能,感興趣的朋友一起看看吧
    2024-08-08
  • 為TextBox裝飾水印與(blur和focus)事件應用

    為TextBox裝飾水印與(blur和focus)事件應用

    為了界面的美觀,有些時候可能需要為文本框(TextBox)裝飾個水?。凰袃煞N狀態(tài),一是blur和focus。因此,我們可以在Javascript寫兩個事件,感興趣的朋友可以了解下
    2013-01-01
  • .NET?Core使用Worker?Service創(chuàng)建服務

    .NET?Core使用Worker?Service創(chuàng)建服務

    這篇文章介紹了.NET?Core使用Worker?Service創(chuàng)建服務的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • asp.net 無刷新附件上傳實現(xiàn)方法

    asp.net 無刷新附件上傳實現(xiàn)方法

    一直以來附件上傳都是個很郁悶的問題,剛開始是利用js添加input file 然后一起提交來實現(xiàn)多文件上傳,在使用163郵箱的時候很是羨慕它的附件上傳部分(選擇完文件就提交,可以多個文件一起上傳,而且還可以獲取上傳進度),這時就很想自己也寫個那樣的東西出來。
    2010-01-01
  • ASP.NET XmlDocument類詳解

    ASP.NET XmlDocument類詳解

    這篇文章主要介紹了ASP.NET XmlDocument類詳解,本文講解了XmlDocument的屬性、XmlDocument的方法、XmlDocument的事件、XmlDocument的使用實例等內(nèi)容,需要的朋友可以參考下
    2015-05-05
  • asp.net Cookie操作類

    asp.net Cookie操作類

    Cookie操作類,本人得還很不錯哦。
    2009-12-12
  • asp.net全局變量的實例方法

    asp.net全局變量的實例方法

    在本篇文章里小編給大家整理的是關于asp.net全局變量的實例方法和實例,需要的朋友們可以學習下。
    2020-02-02
  • .NET微信掃碼支付接入(模式二-NATIVE)

    .NET微信掃碼支付接入(模式二-NATIVE)

    這篇文章主要為大家詳細介紹了.NET微信掃碼支付接入的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評論