亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

委托類型包含三個重要的非公共字段:

  • _target 字段

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

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

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

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

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

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

  • 轉(zhuǎn)換方式

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

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

2.2 捕獲實例成員(通過 this 訪問)

  • 轉(zhuǎn)換方式

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

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

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

  • 轉(zhuǎn)換方式

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

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

3. 委托實例的訂閱與生命周期

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

  • 當委托實例訂閱到常規(guī)委托或事件時,事件源對委托實例持有強引用,從而延長委托實例的生命周期(直至取消訂閱或事件源回收)。

3.2 弱事件訂閱

  • 弱事件模式特點

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

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

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

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

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

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

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

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

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

lambda被轉(zhuǎn)換為委托實例后,當將該委托實例訂閱到常規(guī)委托、事件時,事件源對委托實例進行強引用。

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

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

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

相關(guān)文章

  • C# StreamReader類實現(xiàn)讀取文件的方法

    C# StreamReader類實現(xiàn)讀取文件的方法

    這篇文章主要介紹了C# StreamReader類實現(xiàn)讀取文件的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • C#中改變DataGridView控件邊框顏色的方法

    C#中改變DataGridView控件邊框顏色的方法

    這篇文章主要介紹了C#中改變DataGridView控件邊框顏色的方法,默認的DataGridView邊框顏色很丑,本文用編程方法實現(xiàn)修改DataGridView邊框顏色,需要的朋友可以參考下
    2014-08-08
  • C#正則表達式Regex類的用法

    C#正則表達式Regex類的用法

    這篇文章介紹了C#正則表達式Regex類的用法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-12-12
  • C#泛型類創(chuàng)建與使用的方法

    C#泛型類創(chuàng)建與使用的方法

    這篇文章主要為大家詳細介紹了C#泛型類創(chuàng)建與使用的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • C#線性漸變畫刷LinearGradientBrush用法實例

    C#線性漸變畫刷LinearGradientBrush用法實例

    這篇文章主要介紹了C#線性漸變畫刷LinearGradientBrush用法,實例分析了線性漸變畫刷LinearGradientBrush的相關(guān)使用技巧,需要的朋友可以參考下
    2015-06-06
  • Unity向量按照某一點進行旋轉(zhuǎn)

    Unity向量按照某一點進行旋轉(zhuǎn)

    這篇文章主要為大家詳細介紹了Unity向量按照某一點進行旋轉(zhuǎn),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • 詳解C# ConcurrentBag的實現(xiàn)原理

    詳解C# ConcurrentBag的實現(xiàn)原理

    ConcurrentBag<T>實現(xiàn)了IProducerConsumerCollection<T>接口,該接口主要用于生產(chǎn)者消費者模式下,可見該類基本就是為生產(chǎn)消費者模式定制的。然后還實現(xiàn)了常規(guī)的IReadOnlyCollection<T>類,實現(xiàn)了該類就需要實現(xiàn)IEnumerable<T>、IEnumerable、 ICollection類
    2021-06-06
  • 使用淘寶ip地址庫查ip的示例

    使用淘寶ip地址庫查ip的示例

    這篇文章主要介紹了使用淘寶ip地址庫查ip的示例,需要的朋友可以參考下
    2014-03-03
  • Visual Studio 未能加載各種Package包的解決方案

    Visual Studio 未能加載各種Package包的解決方案

    打開Visual Studio 的時候,總提示未能加載相應(yīng)的Package包,有時候還無法打開項目,各種錯誤提示,怎么解決呢?下面小編給大家?guī)砹薞isual Studio 未能加載各種Package包的解決方案,一起看看吧
    2016-10-10
  • C#使用NPOI讀取excel轉(zhuǎn)為DataSet

    C#使用NPOI讀取excel轉(zhuǎn)為DataSet

    這篇文章主要為大家詳細介紹了C#使用NPOI讀取excel轉(zhuǎn)為DataSet,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評論