程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 詳解C#編程中.NET的弱事宜形式

詳解C#編程中.NET的弱事宜形式

編輯:C#入門知識

詳解C#編程中.NET的弱事宜形式。本站提示廣大學習愛好者:(詳解C#編程中.NET的弱事宜形式)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解C#編程中.NET的弱事宜形式正文


 引言

你能夠曉得,事宜處置是內存洩露的一個罕見起源,它由不再應用的對象存留發生,你或許以為它們應當曾經被收受接管了,但不是,並有充足的來由。

在這個短文中(希冀如斯),我會在 .Net 框架的高低文事宜處置中展現這個成績,以後我會教你這個成績的尺度處理計劃,弱事宜形式。有兩種辦法,即:

  •     “傳統”辦法 (嗯,在 .Net 4.5 前,所以也沒那末老),它完成起來比擬繁瑣
  •     .Net 4.5 框架供給的新辦法,它則是盡其能夠的簡略

(源代碼在 這裡 可供應用。)

從罕見事物開端

在一頭扎進本文焦點內容前,讓我們回想一下在代碼中最常應用的兩個事物:類和辦法。
事宜源

讓我為您引見一個根本但很有效的事宜源類,它最低限制地提醒了足夠的龐雜性來講明這一點:

 

public class EventSource
{
  public event EventHandlerEvent = delegate { };
 
  public void Raise()
  {
    Event(this, EventArgs.Empty);
  }
}

對獵奇誰人奇異的空拜托初始化辦法(delegate { })的人來講,這是一個用來確保事宜總被初始化的技能,如許便可以不用每次在應用它之前都要檢討它能否不為NULL。

觸發渣滓搜集的適用辦法

在.net中,渣滓搜集以一種不肯定的方法觸發。這對我們的試驗很晦氣,我們的試驗須要以一種肯定的方法跟蹤對象的狀況。

所以,我們必需按期觸發本身的渣滓搜集操作,同時防止復制管道代碼,管道代碼曾經在在一個特定的辦法中釋放:
 

static void TriggerGC()
{
  Console.WriteLine("Starting GC.");
 
  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();
 
  Console.WriteLine("GC finished.");
}

固然不是很龐雜,然則假如你不是很熟習這類形式,照樣有需要小小說明一下:

  •     第一個 GC.Collect() 觸發.net的CLR渣滓搜集器,關於擔任清算不再應用的對象,和那些類中沒有終結器(即c#中的析構函數)的對象,CLR渣滓搜集器足夠勝任
  •     GC.WaitForPendingFinalizers() 期待其他對象的終結器履行;我們須要如許做,由於,你將看到我們應用終結器辦法去追蹤我們的對象在甚麼時刻被搜集的
  •     第二個GC.Collect() 確保重生成的對象也被清算了

引入成績

起首讓我們試著經由過程一些實際,最主要的是還有一個演示的贊助,去懂得事宜監聽器有哪些成績。
配景

一個對象要想被作為事宜偵聽器,須要將其實例辦法之一掛號為另外一個可以或許發生事宜的對象(即事宜源)的事宜處置法式,事宜源必需堅持一個到事宜偵聽器對象的援用,以便在事宜產生時挪用此偵聽器的處置辦法。

這很公道,但假如這個援用是一個 強援用,則偵聽器會作為事宜源的一個依附 從而不克不及作為渣滓收受接管,即便援用它的最初一個對象是事宜源。


上面具體圖解在這上面產生了甚麼:

事宜處置成績

這將不是一個成績,假如你可以掌握listener object的性命周期,你可以撤消對事宜源的定閱铛铛你不再須要listener,經常可使用disposable pattern(用後就扔的形式)。

然則假如你不克不及在listener性命周期內驗證單點呼應,在肯定性的方法中你不克不及把它處置失落,你必需依附GC處置...這將從不會斟酌你所預備的對象,只需事宜源還存在著!

例子

實際都是好的,但照樣讓我們看看成績和真實的代碼。

這是我們大膽的時光監聽器,還有點老練,我們很快曉得為何:
 

public class NaiveEventListener
{
  private void OnEvent(object source, EventArgs args)
  {
    Console.WriteLine("EventListener received event.");
  }
 
  public NaiveEventListener(EventSource source)
  {
    source.Event += OnEvent;
  }
 
  ~NaiveEventListener()
  {
    Console.WriteLine("NaiveEventListener finalized.");
  }
}

用一個簡略例子來看看怎樣完成運作:
 

Console.WriteLine("=== Naive listener (bad) ===");
 
EventSource source = new EventSource();
 
NaiveEventListener listener = new NaiveEventListener(source);
 
source.Raise();
 
Console.WriteLine("Setting listener to null.");
listener = null;
 
TriggerGC();
 
source.Raise();
 
Console.WriteLine("Setting source to null.");
source = null;
 
TriggerGC();

輸入:

 

EventListener received event.
Setting listener to null.
Starting GC.
GC finished.
EventListener received event.
Setting source to null.
Starting GC.
NaiveEventListener finalized.
GC finished.

讓我們剖析下這個運作流程:

  •     “EventListener received event.“:這是我們挪用 “source.Raise()”的成果; perfect, seems like we're listening.
  •     “Setting listener to null.“: 我們把當地事宜監聽器對象援用賦空值,如許應當可讓渣滓收受接管器收受接管了.
  •     “Starting GC.“: 渣滓收受接管開端.
  •     “GC finished.“: 渣滓收受接管開端, 然則 然則我們的事宜監聽器沒有被收受接管器收受接管, 如許就證實了事宜監聽器的析構函數沒有被挪用。
  •     “EventListener received event.“: 第二次挪用 “source.Raise()”來確認,發明這監聽器還在世。
  •     “Setting source to null.“: 我們在賦空值給事宜的原對象.
  •     “Starting GC.“: 第二次渣滓收受接管.
  •     “NaiveEventListener finalized.“: 這一次老練的事宜監聽終究被收受接管了,遲到總好過沒有.
  •     “GC finished.“:第二次渣滓收受接管完成.


結論:確切有一個隱蔽的對事宜監聽器的強援用,目標是避免它在事宜源被收受接管之前被收受接管!

願望有針對此成績的尺度處理計劃:讓事宜源可以經由過程弱援用來援用偵聽器,在事宜源存在時也能夠收受接管偵聽器對象。

這裡有一個尺度的形式及其在.NET框架上的完成:弱事宜形式(http://msdn.microsoft.com/en-us/library/aa970850.aspx)。 And there is a standard pattern and its implementation in the .Net framework: the weak event pattern.

弱事宜形式

讓我們看看在.NET中若何敷衍這個成績,

平日有跨越一種辦法去做,然則在這類情形下可以直接決議:

  •     假如你正在應用 .Net 4.5 ,那末你將從簡略的完成受害
  •     別的,你必需依附一點工資的技能手腕

傳統方法

  •     WeakEventManager 是一切形式管道的封裝
  •     IWeakEventListener 是管道,它許可一個組件銜接到WeakEventManager管件

(這兩個位於WindowBase法式集,你將須要參考你本身的假如你不在開辟WPF項目,你應當精確的參考WindowBase)


是以這有兩步處置.

起首經由過程繼續WeakEventManager來完成一個自界說事宜治理器:

  •     重寫 StartListening 和 StopListening 辦法,分離注冊一個新的handler和刊出一個已存在的; 它們將被WeakEventManager基類應用。
  •     供給兩個辦法來拜訪listener列表, 定名為 “AddListener” 和 “RemoveListener “,給自界說事宜治理器的應用者應用。
  •     經由過程在自界說事宜治理器上裸露一個靜態屬性,供給一個方法去取得以後線程的事宜治理器。
  • 以後使listenr完成IWeakEventListenr接口:
  •     完成 ReceiveWeakEvent 辦法
  •     測驗考試行止理這個事宜
  •     假如無誤的處置功德件,將前往true


有許多要說的,然則可以絕對地轉換成一些代碼:

起首是自界說弱事宜治理器:
 

public class EventManager : WeakEventManager
{
  private static EventManager CurrentManager
  {
    get
    {
      EventManager manager = (EventManager)GetCurrentManager(typeof(EventManager));
 
      if (manager == null)
      {
        manager = new EventManager();
        SetCurrentManager(typeof(EventManager), manager);
      }
 
      return manager;
    }
  }
 
 
  public static void AddListener(EventSource source, IWeakEventListener listener)
  {
    CurrentManager.ProtectedAddListener(source, listener);
  }
 
  public static void RemoveListener(EventSource source, IWeakEventListener listener)
  {
    CurrentManager.ProtectedRemoveListener(source, listener);
  }
 
  protected override void StartListening(object source)
  {
    ((EventSource)source).Event += DeliverEvent;
  }
 
  protected override void StopListening(object source)
  {
    ((EventSource)source).Event -= DeliverEvent;
  }
}

以後是事宜listener:
 

public class LegacyWeakEventListener : IWeakEventListener
{
  private void OnEvent(object source, EventArgs args)
  {
    Console.WriteLine("LegacyWeakEventListener received event.");
  }
 
  public LegacyWeakEventListener(EventSource source)
  {
    EventManager.AddListener(source, this);
  }
 
  public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
  {
    OnEvent(sender, e);
 
    return true;
  }
 
  ~LegacyWeakEventListener()
  {
    Console.WriteLine("LegacyWeakEventListener finalized.");
  }
}

檢討下:
 

Console.WriteLine("=== Legacy weak listener (better) ===");
 
EventSource source = new EventSource();
 
LegacyWeakEventListener listener = new LegacyWeakEventListener(source);
 
source.Raise();
 
Console.WriteLine("Setting listener to null.");
listener = null;
 
TriggerGC();
 
source.Raise();
 
Console.WriteLine("Setting source to null.");
source = null;
 
TriggerGC();

輸入:
 

LegacyWeakEventListener received event.
Setting listener to null.
Starting GC.
LegacyWeakEventListener finalized.
GC finished.
Setting source to null.
Starting GC.
GC finished.

異常好,它起感化了,我們的事宜listener對象如今可以在第一次GC裡准確的析構,即便事宜源對象還存活,不再洩漏內存了.

然則要寫一堆代碼就為了一個簡略的listener,想象一下你有一堆如許的listener,你必需要為每一個類型的寫一個弱事宜治理器!

假如你很善於代碼重構,你可以發明一個聰慧的方法去重構一切通用的代碼.

在.Net 4.5 湧現之前,你必需本身完成弱事宜治理器,然則如今,.Net供給一個尺度的處理計劃來處理這個成績了,如今就往返顧下吧!

 .Net 4.5 方法

.Net 4.5 已引見了一個新的泛型版本的遺留WeakEventManager: WeakEventManager<TEventSource, TEventArgs>.

(這個類可以在WindowsBase聚集.)

多虧了 .Net WeakEventManager<TEventSource, TEventArgs> 本身處置泛型, 不消去一個個完成新事宜治理器.

並且代碼還簡略和可讀:
 

public class WeakEventListener
{
  private void OnEvent(object source, EventArgs args)
  {
    Console.WriteLine("WeakEventListener received event.");
  }
 
  public WeakEventListener(EventSource source)
  {
    WeakEventManager.AddHandler(source, "Event", OnEvent);
  }
 
  ~WeakEventListener()
  {
    Console.WriteLine("WeakEventListener finalized.");
  }
}

簡略的一行代碼,真簡練.

其他完成的應用也是類似的, 就是裝入一切器械到事宜listener類裡:

 

Console.WriteLine("=== .Net 4.5 weak listener (best) ===");
 
EventSource source = new EventSource();
 
WeakEventListener listener = new WeakEventListener(source);
 
source.Raise();
 
Console.WriteLine("Setting listener to null.");
listener = null;
 
TriggerGC();
 
source.Raise();
 
Console.WriteLine("Setting source to null.");
source = null;
 
TriggerGC();

輸入也是確定准確的:
 

WeakEventListener received event.
Setting listener to null.
Starting GC.
WeakEventListener finalized.
GC finished.
Setting source to null.
Starting GC.
GC finished.

預期成果也跟之前一樣,還有甚麼成績?!

結論

正如你看到的,在.Net上完成弱事宜形式 是非常直接, 特殊在 .Net 4.5.

假如你沒有效.Net 4.5來完成,將須要一堆代碼, 你能夠不去用任何形式而是直接應用C# (+= and -=), 看看能否有內存成績,假如留意到洩漏,還須要花需要的時光去完成一個。

然則用 .Net 4.5, 它是自在和簡練,並且由框架治理, 你可以毫無掛念的選擇它, 雖然沒有 C# 語法 “+=” 和 “-=” 的酷, 然則語義是清楚的,這才是最主要的.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved