最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#?委托與?Lambda?表達(dá)式轉(zhuǎn)換機(jī)制及弱事件模式下的生命周期詳解

 更新時(shí)間:2025年02月27日 08:48:53   作者:陳百川  
本文介紹了C#委托和Lambda表達(dá)式的工作原理,包括委托的內(nèi)部結(jié)構(gòu)、Lambda表達(dá)式的轉(zhuǎn)換機(jī)制以及弱事件模式下的生命周期管理,感興趣的朋友一起看看吧

1. 委托內(nèi)部結(jié)構(gòu)

委托類(lèi)型包含三個(gè)重要的非公共字段:

  • _target 字段

    • 靜態(tài)方法包裝:當(dāng)委托包裝一個(gè)靜態(tài)方法時(shí),該字段為 null。
    • 實(shí)例方法包裝:當(dāng)委托包裝實(shí)例方法時(shí),該字段引用回調(diào)方法所操作的對(duì)象。
  • _methodPtr 字段

    • 標(biāo)識(shí)委托要調(diào)用的方法。
  • _invocationList 字段

    • 存儲(chǔ)委托鏈(即內(nèi)部委托數(shù)組),用于實(shí)現(xiàn)多播委托。

2. Lambda 表達(dá)式轉(zhuǎn)換為委托實(shí)例

C# 編譯器會(huì)將 lambda 表達(dá)式轉(zhuǎn)換成相應(yīng)的委托實(shí)例,具體轉(zhuǎn)換方式依賴(lài)于 lambda 是否捕獲外部數(shù)據(jù)。

2.1 不捕獲任何外部數(shù)據(jù)

  • 轉(zhuǎn)換方式

    • 將 lambda 表達(dá)式生成為私有的靜態(tài)函數(shù)(編譯器自動(dòng)生成方法名)。
    • 同時(shí)生成一個(gè)委托類(lèi)型的靜態(tài)字段用于緩存委托實(shí)例。
  • 委托實(shí)例創(chuàng)建與緩存

    • 當(dāng)調(diào)用包含 lambda 的方法時(shí),先檢查靜態(tài)字段是否為 null。
    • 若不為 null,則直接返回緩存的委托實(shí)例;若為 null,則創(chuàng)建新的委托實(shí)例,并賦值給靜態(tài)字段。
    • 這種方式確保委托實(shí)例只創(chuàng)建一次,被靜態(tài)字段引用后不會(huì)被回收。

2.2 捕獲實(shí)例成員(通過(guò) this 訪問(wèn))

  • 轉(zhuǎn)換方式

    • 將 lambda 表達(dá)式生成為私有的實(shí)例函數(shù)(編譯器自動(dòng)生成方法名)。
  • 委托實(shí)例創(chuàng)建

    • 每次調(diào)用包含 lambda 的方法時(shí),都會(huì)實(shí)時(shí)創(chuàng)建一個(gè)新的委托實(shí)例,包裝該實(shí)例函數(shù)。

2.3 捕獲非實(shí)例成員(例如局部變量)

  • 轉(zhuǎn)換方式

    • 編譯器生成一個(gè)私有的輔助閉包類(lèi)(通常命名為 “<>c__DisplayClassXXX”)。
    • 輔助類(lèi)中包含公開(kāi)字段,用于保存捕獲的局部變量(或其他非實(shí)例數(shù)據(jù))。
    • 在該輔助類(lèi)中,將 lambda 表達(dá)式轉(zhuǎn)換為公開(kāi)的實(shí)例函數(shù),該方法通過(guò)訪問(wèn)輔助類(lèi)字段來(lái)使用捕獲的數(shù)據(jù)。
  • 委托與閉包實(shí)例的創(chuàng)建

    • 每次調(diào)用包含 lambda 的方法時(shí),都會(huì)生成一個(gè)輔助類(lèi)實(shí)例。
    • 然后創(chuàng)建一個(gè)委托實(shí)例,其 _target 字段指向該輔助類(lèi)實(shí)例。
    • 注意:在循環(huán)中容易產(chǎn)生閉包陷阱——盡管每次迭代可能創(chuàng)建多個(gè)輔助類(lèi)實(shí)例與委托實(shí)例,但這些輔助類(lèi)實(shí)例中的捕獲字段指向同一塊內(nèi)存(即共享同一循環(huán)變量)。由于 lambda 表達(dá)式通常在循環(huán)結(jié)束后執(zhí)行,所有回調(diào)看到的循環(huán)變量值往往都是最后一次迭代的狀態(tài)。
    • 另外,不同版本的 C# 對(duì)于循環(huán)中輔助類(lèi)實(shí)例的創(chuàng)建可能存在差異,有的版本可能只在進(jìn)入方法時(shí)創(chuàng)建一次,而有的版本則每次迭代都創(chuàng)建新的實(shí)例。至于委托實(shí)例,我猜測(cè)每次迭代都會(huì)創(chuàng)建一個(gè)新的委托實(shí)例(否則作為字典鍵時(shí)可能會(huì)出現(xiàn)重復(fù)的問(wèn)題),但《CLR Via C# 第四版》中示例代碼(17.7.3節(jié),中文版365頁(yè))顯示委托實(shí)例只創(chuàng)建了一次,這里感覺(jué)有點(diǎn)問(wèn)題,有興趣的朋友可以分析一下。

3. 委托實(shí)例的訂閱與生命周期

3.1 常規(guī)委托/事件訂閱

  • 當(dāng)委托實(shí)例訂閱到常規(guī)委托或事件時(shí),事件源對(duì)委托實(shí)例持有強(qiáng)引用,從而延長(zhǎng)委托實(shí)例的生命周期(直至取消訂閱或事件源回收)。

3.2 弱事件訂閱

  • 弱事件模式特點(diǎn)

    • 委托實(shí)例的生命周期至少大于其 _target 引用的對(duì)象的生命周期。
  • 實(shí)現(xiàn)機(jī)制

    • 利用 ConditionalWeakTable<TKey, TValue> 進(jìn)行關(guān)聯(lián):
      • 將 _target 引用的對(duì)象作為 key。
      • 將委托實(shí)例作為 value。
    • ConditionalWeakTable 對(duì) key 使用弱引用,但對(duì) value 使用強(qiáng)引用,保證只要 key 存在,對(duì)應(yīng)的 value 就不會(huì)被回收。
  • 訂閱流程

    • 當(dāng)委托實(shí)例通過(guò) WeakEventManager<TEventSource, TEventArgs> 訂閱弱事件時(shí),內(nèi)部會(huì)通過(guò) Delegate.Target 獲取 _target 引用的對(duì)象,并將該對(duì)象與委托實(shí)例關(guān)聯(lián)到 ConditionalWeakTable 中,從而確保委托實(shí)例的生命周期至少與 _target 對(duì)象一致。

上面用工具重新排版了下,下面是我編輯的原文:

委托類(lèi)型包含三個(gè)重要的非公共字段:_target字段,當(dāng)委托實(shí)例包裝一個(gè)靜態(tài)方法時(shí),該字段為空;包裝實(shí)例方法時(shí),這個(gè)字段引用回調(diào)方法要操作的對(duì)象。_methodPtr字段標(biāo)識(shí)要回調(diào)的方法。_invocationList字段引用委托數(shù)組。

C#編譯器將lambda方法替換為對(duì)應(yīng)的委托實(shí)例。

當(dāng)lambda不獲取任何外部數(shù)據(jù)時(shí),調(diào)用只創(chuàng)建一次委托實(shí)例并緩存:C#編譯器將lambda表達(dá)式生成為私有的靜態(tài)函數(shù)(編譯器自動(dòng)取名的方法),并生成一個(gè)委托類(lèi)型的靜態(tài)字段。當(dāng)調(diào)用使用lambda的方法時(shí),先判斷自動(dòng)生成的靜態(tài)字段是否為空,不為空則直接返回靜態(tài)字段引用的委托實(shí)例,為空則先創(chuàng)建一個(gè)包裝靜態(tài)函數(shù)的委托實(shí)例賦值給靜態(tài)委托字段。(這導(dǎo)致被靜態(tài)字段引用的委托實(shí)例不會(huì)被釋放,但委托實(shí)例只會(huì)被創(chuàng)建一次)。

當(dāng)lambda獲取實(shí)例成員時(shí)(通過(guò)this指針訪問(wèn)),每次調(diào)用都創(chuàng)建新的委托實(shí)例:C#編譯器將lambda表達(dá)式生成為私有的實(shí)例函數(shù)(編譯器自動(dòng)取名的方法)。每次調(diào)用使用lambda的方法時(shí)都實(shí)時(shí)創(chuàng)建一個(gè)委托實(shí)例包裝該自動(dòng)生成的實(shí)例函數(shù)。

當(dāng)lambda獲取非實(shí)例成員時(shí)(不通過(guò)當(dāng)前實(shí)例的this指針訪問(wèn),比如局部變量),C#編譯器創(chuàng)建一個(gè)私有的輔助類(lèi),輔助類(lèi)擁有對(duì)應(yīng)的公開(kāi)字段引用非實(shí)例成員,在輔助類(lèi)中將將lambda表達(dá)式生成為公開(kāi)的實(shí)例函數(shù)。每次調(diào)用使用lambda的方法時(shí)都生成輔助類(lèi)實(shí)例,引用相同的非實(shí)例成員,然后創(chuàng)建委托實(shí)例傳入輔助類(lèi)實(shí)例。(循環(huán)中的閉包陷阱就在于循環(huán)中雖然創(chuàng)建了多個(gè)輔助類(lèi)實(shí)例與委托實(shí)例,但不同輔助類(lèi)實(shí)例引用的非實(shí)例成員是同一塊內(nèi)存。lambda 表達(dá)式是在循環(huán)中創(chuàng)建,但其執(zhí)行往往是在循環(huán)結(jié)束后才發(fā)生,所以所有回調(diào)看到的循環(huán)變量都是最終狀態(tài)。并且不同版本C#實(shí)現(xiàn)在循環(huán)中可能并沒(méi)有創(chuàng)建循環(huán)次數(shù)的輔助類(lèi)實(shí)例,而是在進(jìn)入方法時(shí)只創(chuàng)建一次。我猜測(cè)創(chuàng)建了循環(huán)次數(shù)的委托實(shí)例,不然作為字典的鍵時(shí)就應(yīng)該出錯(cuò)了。但CLR Via C#第四版給的示例代碼中委托實(shí)例只創(chuàng)建了一次,這可能有點(diǎn)問(wèn)題,有興趣的朋友可以分析一下。

lambda被轉(zhuǎn)換為委托實(shí)例后,當(dāng)將該委托實(shí)例訂閱到常規(guī)委托、事件時(shí),事件源對(duì)委托實(shí)例進(jìn)行強(qiáng)引用。

當(dāng)將該委托實(shí)例訂閱到弱事件時(shí),存在有意思的現(xiàn)象:委托實(shí)例的生命周期最起碼大于_target引用的對(duì)象的生命周期。這是通過(guò)ConditionalWeakTable<TKey, TValue>實(shí)現(xiàn)的,通過(guò)將_target引用的對(duì)象設(shè)置為key、將委托實(shí)例設(shè)置為value。該類(lèi)負(fù)責(zé)數(shù)據(jù)間的關(guān)聯(lián),它對(duì)key是弱引用,但保證只要key在內(nèi)存中,value就一定在內(nèi)存中。

委托實(shí)例通過(guò)WeakEventManager<TEventSource, TEventArgs>訂閱弱事件時(shí),WeakEventManager<TEventSource, TEventArgs>內(nèi)部會(huì)通過(guò)Delegate.Target拿到委托實(shí)例中_target引用的對(duì)象,作為ConditionalWeakTable的key,委托實(shí)例作為ConditionalWeakTable的value進(jìn)行關(guān)聯(lián)。這樣就保證了弱事件模式下委托實(shí)例的生命周期至少大于_target引用的對(duì)象的生命周期。

public void AddHandler(Delegate handler)
{
    Invariant.Assert(_users == 0, "Cannot modify a ListenerList that is in use");
    object obj = handler.Target;
    if (obj == null)
    {
        obj = StaticSource;
    }
    _list.Add(new Listener(obj, handler));
    AddHandlerToCWT(obj, handler);
}
private void AddHandlerToCWT(object target, Delegate handler)
{
    if (!_cwt.TryGetValue(target, out var value))
    {
        _cwt.Add(target, handler);
        return;
    }
    List<Delegate> list = value as List<Delegate>;
    if (list == null)
    {
        Delegate item = value as Delegate;
        list = new List<Delegate>();
        list.Add(item);
        _cwt.Remove(target);
        _cwt.Add(target, list);
    }
    list.Add(handler);
}

到此這篇關(guān)于C# 委托與 Lambda 表達(dá)式轉(zhuǎn)換機(jī)制及弱事件模式下的生命周期分析的文章就介紹到這了,更多相關(guān)C# 委托與 Lambda 表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#利用Random得隨機(jī)數(shù)求均值、方差、正態(tài)分布的方法

    C#利用Random得隨機(jī)數(shù)求均值、方差、正態(tài)分布的方法

    這篇文章主要介紹了C#利用Random得隨機(jī)數(shù)求均值、方差、正態(tài)分布的方法,涉及C#數(shù)學(xué)運(yùn)算及概率統(tǒng)計(jì)的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • 探秘C# 6.0 的新特性

    探秘C# 6.0 的新特性

    本文的內(nèi)容包括引入C#6.0中的新的語(yǔ)言特性有哪些. 還有已經(jīng)被引入的代碼名稱(chēng)為 “Roslyn”新編譯器. 編譯器是開(kāi)放源碼的,并且可以從 codeplex 網(wǎng)站的這個(gè)地址下載到源代碼:https://roslyn.codeplex.com/.
    2015-03-03
  • DevExpress之ChartControl用法實(shí)例總結(jié)

    DevExpress之ChartControl用法實(shí)例總結(jié)

    這篇文章主要介紹了DevExpress之ChartControl用法實(shí)例總結(jié),需要的朋友可以參考下
    2014-08-08
  • ASP.NET總結(jié)C#中7種獲取當(dāng)前路徑的方法

    ASP.NET總結(jié)C#中7種獲取當(dāng)前路徑的方法

    本文主要介紹了7種獲取當(dāng)前路徑的方法,并做了代碼演示,分享給大家,感興趣的朋友可以參考一下。
    2016-03-03
  • c#將list類(lèi)型轉(zhuǎn)換成DataTable方法示例

    c#將list類(lèi)型轉(zhuǎn)換成DataTable方法示例

    將List類(lèi)型轉(zhuǎn)換成DataTable的通用方法,大家參考使用吧
    2013-12-12
  • 混合語(yǔ)言編程—C#使用原生的Directx和OpenGL繪圖的方法

    混合語(yǔ)言編程—C#使用原生的Directx和OpenGL繪圖的方法

    本文要說(shuō)的是混合C#和C/C++語(yǔ)言編程,在C#的Winform和WPF下使用原生的Direct和OpenGL進(jìn)行繪圖
    2013-09-09
  • Unity實(shí)現(xiàn)UI漸變效果

    Unity實(shí)現(xiàn)UI漸變效果

    這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)UI漸變效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C#中接口(Interface)的深入詳解

    C#中接口(Interface)的深入詳解

    這篇文章主要給大家介紹了關(guān)于C#中接口(Interface)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • C#實(shí)現(xiàn)文本轉(zhuǎn)語(yǔ)音功能

    C#實(shí)現(xiàn)文本轉(zhuǎn)語(yǔ)音功能

    這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)文本轉(zhuǎn)語(yǔ)音功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 使用JsonConverter處理上傳文件的路徑問(wèn)題

    使用JsonConverter處理上傳文件的路徑問(wèn)題

    我們上傳一個(gè)文件,把文件保存到服務(wù)器上,會(huì)有一個(gè)明確的物理路徑,由于需要從前端訪問(wèn)這個(gè)文件,還需要web服務(wù)器中的一個(gè)虛擬路徑,我們可以使用JsonConverter 來(lái)自動(dòng)處理一下,這篇文章主要介紹了使用JsonConverter處理上傳文件的路徑,需要的朋友可以參考下
    2022-12-12

最新評(píng)論

彰化市| 夹江县| 和龙市| 安宁市| 五华县| 溧阳市| 宜春市| 绵竹市| 同心县| 宜州市| 榆社县| 合山市| 普格县| 宁明县| 新乡市| 遵义县| 涿州市| 莲花县| 儋州市| 金门县| 嫩江县| 和顺县| 远安县| 漠河县| 黎城县| 汤阴县| 奇台县| 新绛县| 丰镇市| 长葛市| 台中市| 乐昌市| 五寨县| 定远县| 宜城市| 青州市| 宁海县| 克山县| 沙田区| 玛沁县| 吴江市|