貧弱な処分について、一言で言えば(パート2)

廃棄に関する以前のレコードの発行後、個別の検討を必要とするいくつかの鋭い質問が議論の中で浮上しました。

要するに、彼らは「なぜそんなに難しいのか」と「子孫がどのように記憶を正しく解放すべきなのか」に要約します。

そして確かに。 2つの実装があります。 シンプル:
DBConnection conn = new DBConnection();
try
{
conn.Open();
//...
}
finally
{
conn.Close();
}


* This source code was highlighted with Source Code Highlighter .
そして複雑な:
public class MyResource: IDisposable
{
private bool disposed = false ;

// IDisposable
//
//
public void Dispose()
{
Dispose( true ); //
GC.SuppressFinalize( this ); // , GC
}

//
~MyClass()
{
Dispose( false ); // GC
}

//
private void Dispose( bool manual)
{
if (! this .disposed)
{
if (manual)
{
// managed
//...

// - Dispose
// managed -
// Dispose,
// Dispose GC
}

// unmanaged
//...

disposed = true ;
}
}
}


* This source code was highlighted with Source Code Highlighter .
さらに、2つのバージョンに複雑な実装が存在します。 最初のオプションはこのリンクでMSDNによって提供され、2番目のオプション(ここでは継承をサポートする)が提供されます。 2番目の実装は次のようになります。
public class Base: IDisposable
{
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}

protected virtual void Dispose( bool disposing)
{
if (disposing)
{
// Dispose managed
}
// unmanaged
}

~Base()
{
Dispose ( false );
}
}

//
public class Derived: Base
{
protected override void Dispose( bool disposing)
{
if (disposing)
{
// Dispose managed
}
// unmanaged

// Dispose(bool)
base .Dispose(disposing);
}

// Dispose
// - .
}


* This source code was highlighted with Source Code Highlighter .
違いは、簡単にわかるように、Dispose(bool)メソッドが閉じられたものから保護された仮想のものに変わったことだけです。 なぜそんなに難しいのか、考えてみましょう そして、なぜこれを知る必要があるのですか?

まず、2番目の質問に答えます。IDisposableインターフェイスを既に実装している標準クラスから継承する場合、リソースを解放するための提案されたオプションを知っている必要があります。 例はSystem.Windows.Forms.UserControlで、 既に Disposeメソッドが実装されています。

単純な場合:UserControlからコントロールを作成しました。 そして、コントロールはアンマネージリソースを所有しています。 たとえば、彼はOpenGLを使用して画像を表示する方法を知っているため、レンダリングコンテキストを制御する必要があります。 そして、管理するだけでなく、正しくリリースします。 この場合、親クラスでDisposeメソッドをオーバーライドすると、リソースの割り当て解除スキームに干渉しますが、これは明らかに悪いことです。

この場合、MSが提案したリソース解放メカニズムと対話することにより、問題を解決できます。 そして、彼らの解決策は非常にシンプルに見えます:

1)「今すぐ」手動でクリーニングする方法があります:破棄()
2)「ガベージをクリーンアップする時間です」(GCから呼び出される)をクリーニングする方法があります:デストラクタ
3)それと別の方法の両方が、別の方法で取り出されるクリーニングを実行する必要があります:Dispose(bool)

クリーニングを実行するDispose(bool)メソッドは、「今すぐ、すぐに」と「ゴミを削除するとき」からクリーニングを区別できる入力を受け取ります。 なんで? そして、クラスはアンマネージリソースを所有するマネージメンバー変数を所有できるためです。 そして、管理されていないリソースを解放できるように、Disposeを呼び出す必要もあります。 GCがクリーニングを行う状況とは異なります。 この場合、「今すぐ」リソースを解放する必要はなく、GCに頼ることができます。

MSDNからの継承の場合に提案された実装には、もう1つの機能があります。破棄されたフラグ変数はありません。 つまり 複数のDispose呼び出しに対する保護はありません。 これはあまり美しくありません。 この記事で提案されているわずかに異なる実装があります。
using System;

namespace RSDN
{
public abstract class DisposableType: IDisposable
{
bool disposed = false ;

~DisposableType()
{
if (!disposed)
{
disposed = true ;
Dispose( false );
}
}

public void Dispose()
{
if (!disposed)
{
disposed = true ;
Dispose( true );
GC.SuppressFinalize( this );
}
}

public void Close()
{
Dispose();
}

protected virtual void Dispose( bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
}


* This source code was highlighted with Source Code Highlighter .

Source: https://habr.com/ru/post/J57432/


All Articles