はい、それは非常に良い考えです。イベント パブリッシャーは、イベント サブスクライバーへの参照を保持します。これにより、サブスクライバーがガベージ コレクションされるのを防ぐことができます。(イベント ハンドラーはガベージ コレクションの発生を停止しますか?を参照してください。 )
さらに、イベント ハンドラーが解放するリソースを使用する場合、イベント ハンドラー (イベント発行者によって引き続き呼び出されます) は、リソースが解放された後に例外を生成する可能性があります。
したがって、特に非同期または複数のスレッドを使用している場合は、リソースを解放する前にイベントから登録を解除することが重要です。これは、一部のシナリオでは、リソースを解放してからそのイベントを登録解除するまでの間にイベントが発生する可能性があるためです。 .
次のコードはこれを示しています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ReleaseEvents
{
class Program
{
public static event EventHandler SomethingHappened;
static void Main( string[] args )
{
using ( var l_dependent = new Dependent() )
{
SomethingHappened( null, EventArgs.Empty );
}
// Just to prove the point, garbage collection
// will not clean up the dependent object, even
// though it has been disposed.
GC.Collect();
try
{
// This call will cause the disposed object
// (which is still registered to the event)
// to throw an exception.
SomethingHappened( null, EventArgs.Empty );
}
catch ( InvalidOperationException e )
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine( e.ToString() );
}
Console.ReadKey( true );
}
}
class Dependent : IDisposable
{
private object _resource;
public Dependent()
{
Program.SomethingHappened += Program_SomethingHappened;
_resource = new object();
}
private void Program_SomethingHappened( object sender, EventArgs e )
{
if ( _resource == null )
throw new InvalidOperationException( "Resource cannot be null!" );
Console.WriteLine( "SomethingHappened processed successfully!" );
}
public void Dispose()
{
_resource = null;
}
}
}
SomethingHappened
イベントが2 回目に発生すると、Dependent
クラスは InvalidOperationException をスローします。これを防ぐには、イベントの登録を解除する必要があります。
class Dependent : IDisposable
{
// ...
public void Dispose()
{
Program.SomethingHappened -= Program_SomethingHappened;
_resource = null;
}
}
私が最初に MVVM アーキテクチャを実装しようとしたときに、実際にこの問題に遭遇しました。ViewModel を切り替えていたとき、唯一の参照 (ActiveViewModel プロパティ) だと思っていたものを解放しただけでした。ViewModel がサブスクライブされたイベントがそれをメモリに保持していることに気付きませんでした。アプリケーションの実行時間が長くなるにつれて、ますます遅くなりました。最終的に、私がリリースしたと思っていた ViewModel が、実際にはイベントを処理し続けていたことに気付きました (費用がかかります)。この問題を解決するには、イベント ハンドラーを明示的に解放する必要がありました。