マネージド オブジェクトは通常、ガベージ コレクターによって、AppDomain の静的データ、実行中のスレッドのスタック、およびそれらが GC ヒープ上で参照するオブジェクトをウォークすることによって検出されます。ただし、コレクター自体が、ライブで収集されるべきではないオブジェクトへの参照を見つけることができない特定のシナリオがあります。
これは、アンマネージ コードがそのようなオブジェクトを使用する場合に発生します。このコードは jit されていないため、GC にはオブジェクト参照を検出する適切な方法がありません。そのようなコードのスタック フレームを確実に検査してポインタを検出することはできません。GC がまだ参照を認識していることを確認する必要があります。これが GCHandle の機能です。CLR 内に構築された GC ハンドルの専用テーブルを使用します。GCHandle.Alloc() を使用してこのテーブルにエントリを割り当て、後で GCHandle.Free() を使用して再度解放します。ガベージ コレクターは、コレクションを実行するときに、このテーブルのエントリを自身で発見したオブジェクトのグラフに追加するだけです。
C++/CLI の gcroot<> キーワードは、この例です。これにより、単純な未加工のポインタを格納できるアンマネージ コードを記述し、必要に応じてそれをマネージ オブジェクト参照に戻すことができます。GCHandle.ToIntPtr() メソッドはそのポインターを生成し、FromIntPtr() はオブジェクト参照を回復します。GCHandle テーブル エントリは、アンマネージ コードが Free() を明示的に呼び出すまで、オブジェクトが収集されないようにします。通常、C++ デストラクタによって実行されます。
GCHandle は、オブジェクトを固定する機能もサポートしています。これは、ネイティブ コードにマーシャリングする必要があり、pinvoke マーシャラーがジョブを完了できない場合に使用されます。GCHandle.AddrOfPinnedObject() の戻り値を渡します。通常は常に WeakReference クラスを使用しますが、弱参照を実装します。また、非同期固定ハンドルを実装しているため、直接公開されていない機能である、I/O 完了時に固定 I/O バッファーを自動的に固定解除できます。そうです、GCHandle は単に参照を保持するだけではありません。
また、デリゲートに関する質問で重要なのは、CLR がデリゲートを使用してネイティブ コードを呼び出すことをサポートしていることです。基礎となるヘルパー関数は Marshal.GetFunctionPointerForDelegate() です。これにより、ネイティブ コードがマネージ コードにコールバックできるマシン コード スタブが動的に構築されます。これにはデリゲート オブジェクトを参照し続ける必要があり、そのために GCHandle.Alloc() がよく使用されます。これが唯一の方法ではありません。デリゲートを静的変数に格納することは、デリゲート オブジェクトがガベージ コレクションされないようにする別の方法です。それ以外の場合、そのデリゲートを固定する必要はありません。GCHandleType.Normal は問題ありません。
これはどの .NET プログラムでもよく使用されますが、そのほとんどは目に見えません。特に、.NET フレームワーク コード自体と pinvoke マーシャラーで。GCHandle を使用してデリゲート オブジェクトを保護する必要があるのは、ネイティブ コードが関数ポインターを格納し、後でコールバックを作成できる場合のみです。