事件注冊與潛在隱患
首先看一段模擬代碼 class EnterpriseComponent
{
public event EventHandler OnEvent;
~EnterpriseComponent()
{
Console.WriteLine("EnterpriseComponent Destroy...");
}
public void DoEvent()
{
if (OnEvent != null)
OnEvent(this, EventArgs.Empty);
}
}
class Client
{
~Client()
{
Console.WriteLine("Client Destroy...");
}
public void OnEvent(object sender, EventArgs e)
{
Console.WriteLine("Event...");
}
}
class Program
{
static void Main(string[] args)
{
EnterpriseComponent eco = new EnterpriseComponent();
Client client = new Client();
// 注冊事件
eco.OnEvent += new EventHandler(client.OnEvent);
// 執行事件
eco.DoEvent();
// 模擬Client超出作用範圍,等待垃圾回收。
client = null;
// 執行垃圾回收
Console.WriteLine("--GC Strat ---------------");
GC.Collect();
Console.WriteLine("--GC End------------------");
}
}
{
public event EventHandler OnEvent;
~EnterpriseComponent()
{
Console.WriteLine("EnterpriseComponent Destroy...");
}
public void DoEvent()
{
if (OnEvent != null)
OnEvent(this, EventArgs.Empty);
}
}
class Client
{
~Client()
{
Console.WriteLine("Client Destroy...");
}
public void OnEvent(object sender, EventArgs e)
{
Console.WriteLine("Event...");
}
}
class Program
{
static void Main(string[] args)
{
EnterpriseComponent eco = new EnterpriseComponent();
Client client = new Client();
// 注冊事件
eco.OnEvent += new EventHandler(client.OnEvent);
// 執行事件
eco.DoEvent();
// 模擬Client超出作用範圍,等待垃圾回收。
client = null;
// 執行垃圾回收
Console.WriteLine("--GC Strat ---------------");
GC.Collect();
Console.WriteLine("--GC End------------------");
}
}
輸出:
Event...
--GC Strat ---------------
--GC End------------------
--GC Strat ---------------
--GC End------------------
執行這段代碼,我們可以看出,Client並沒有被垃圾回收。
接下來,我們添加一行代碼,再來一次。
static void Main(string[] args)
{
EnterpriseComponent eco = new EnterpriseComponent();
Client client = new Client();
// 注冊事件
eco.OnEvent += new EventHandler(client.OnEvent);
// 執行事件
eco.DoEvent();
eco.OnEvent -= new EventHandler(client.OnEvent); // <----- 添加的代碼,取消事件注冊!!!
// 模擬Client超出作用範圍,等待垃圾回收。
client = null;
// 執行垃圾回收
Console.WriteLine("--GC Strat ---------------");
GC.Collect();
Console.WriteLine("--GC End------------------");
}
{
EnterpriseComponent eco = new EnterpriseComponent();
Client client = new Client();
// 注冊事件
eco.OnEvent += new EventHandler(client.OnEvent);
// 執行事件
eco.DoEvent();
eco.OnEvent -= new EventHandler(client.OnEvent); // <----- 添加的代碼,取消事件注冊!!!
// 模擬Client超出作用範圍,等待垃圾回收。
client = null;
// 執行垃圾回收
Console.WriteLine("--GC Strat ---------------");
GC.Collect();
Console.WriteLine("--GC End------------------");
}
輸出:
Event...
--GC Strat ---------------
Client Destroy...
--GC End------------------
--GC Strat ---------------
Client Destroy...
--GC End------------------
可以看出,Client在取消事件注冊後被GC回收。其原因就是如果沒有取消事件注冊,那麼委托鏈表中的強引用就不會失效,因此GC也就無法回收。相關細節可以參考SDK中有關事件和委托的說明,在此不做詳述。
注意:隻要一個對象仍然登記有另一個對象的事件,該對象就不可能被執行垃圾回收。建議配合IDisposeable接口,在對象失效前取消事件注冊,否則可能造成潛在的內存泄漏。
最後更新:2017-04-02 00:06:28