2

Dispose経由の関数を持つ C# クラスがありますIDisposable。ブロック内で使用することを意図しているusingため、処理する高価なリソースをすぐに解放できます。

問題は、 が呼び出される前に例外がスローされたときにバグが発生し、プログラマーがorDisposeの使用を怠ったことです。usingfinally

C++ では、これについて心配する必要はありませんでした。クラスのデストラクタへの呼び出しは、オブジェクトのスコープの最後に自動的に挿入されます。そのような事態を回避する唯一の方法は、 new 演算子を使用してオブジェクトをポインターの背後に保持することですが、プログラマーにとって余分な作業が必要になるのは、using.

usingブロックをC#で自動的に使用する方法はありますか?

どうもありがとう。

アップデート:

ファイナライザーの回答を受け入れない理由を説明したいと思います。これらの答えは技術的には正しいですが、C++ スタイルのデストラクタではありません。

これが私が見つけたバグであり、本質的なものに縮小されています...

try
{
    PleaseDisposeMe a = new PleaseDisposeMe();
    throw new Exception();
    a.Dispose();
}
catch (Exception ex)
{
    Log(ex);
}

// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();

使用FXCopすることは素晴らしい提案ですが、それが私の唯一の答えである場合、私の質問は C# の人々への嘆願になるか、C++ を使用する必要があります。ネストされた 20 の using ステートメントはありますか?

4

7 に答える 7

6

私が働いている場所では、次のガイドラインを使用しています。

  • 各 IDisposable クラスにはファイナライザーが必要です
  • IDisposable オブジェクトを使用する場合は常に、"using" ブロック内で使用する必要があります。唯一の例外は、オブジェクトが別のクラスのメンバーである場合です。この場合、包含クラスは IDisposable である必要があり、'Dispose' の独自の実装でメンバーの 'Dispose' メソッドを呼び出す必要があります。これは、別の「Dispose」メソッド内を除いて、開発者が「Dispose」を呼び出すべきではないことを意味し、質問で説明されているバグを排除します。
  • 各ファイナライザーのコードは、ファイナライザーが呼び出されたことを通知する警告/エラー ログで始まる必要があります。こうすることで、コードをリリースする前に上記のようなバグを発見できる可能性が非常に高くなり、システムで発生するバグのヒントになる可能性があります。

私たちの生活を楽にするために、インフラストラクチャには SafeDispose メソッドもあります。これは、try-catch ブロック内でその引数の Dispose メソッドを呼び出します (エラー ログを使用)。ただし、Dispose メソッドは例外をスローすることは想定されていません。 )。

参照: IDisposable に関するChris Lyonの提案

編集: @Quarrelsome: オブジェクトが破棄された場合に「再破棄」されないように、「Dispose」内で GC.SuppressFinalize を呼び出す必要があります。

通常、オブジェクトがすでに破棄されているかどうかを示すフラグを保持することもお勧めします。次のパターンは通常かなり良いです:

class MyDisposable: IDisposable {
    public void Dispose() {
        lock(this) {
            if (disposed) {
                return;
            }

            disposed = true;
        }

        GC.SuppressFinalize(this);

        // Do actual disposing here ...
    }

    private bool disposed = false;
}

もちろん、ロックは常に必要というわけではありませんが、クラスがマルチスレッド環境で使用されるかどうかわからない場合は、ロックを保持することをお勧めします。

于 2008-09-06T17:03:13.733 に答える
3

残念ながら、これをコードで直接行う方法はありません。これが社内の問題である場合、この種の問題をキャッチできるさまざまなコード分析ソリューションがあります。FxCopを調べましたか?これにより、これらの状況と、IDisposable オブジェクトがハングしたままになる可能性があるすべてのケースをキャッチできると思います。それが組織外の人々が使用しているコンポーネントであり、FxCop を要求できない場合は、ドキュメントが本当に唯一の手段です:)。

編集:ファイナライザーの場合、これはファイナライズがいつ行われるかを実際に保証するものではありません。したがって、これはあなたにとっての解決策かもしれませんが、状況によって異なります。

于 2008-09-06T16:17:36.433 に答える
2

ベスト プラクティスは、クラスでファイナライザーを使用し、常にusingブロックを使用することです。

ファイナライザは C デストラクタのように見えますが、動作が異なります。

ブロックを入れ子にすることになっているusingため、C# コード レイアウトは既定でブロックを同じ行に配置するようになっています...

using (SqlConnection con = new SqlConnection("DB con str") )
using (SqlCommand com = new SqlCommand( con, "sql query") )
{
    //now code is indented one level
    //technically we're nested twice
}

使用していないときはusing、ボンネットの下で行うことを実行できます。

PleaseDisposeMe a;
try
{
    a = new PleaseDisposeMe();
    throw new Exception();
}
catch (Exception ex) { Log(ex); }  
finally {    
    //this always executes, even with the exception
    a.Dispose(); 
}

マネージ コードを使用すると、C# は自身のメモリの管理が非常に得意です。管理されていないリソースを頻繁に扱っている場合、それほど強力ではありません。

于 2008-09-07T16:25:26.077 に答える
2
~ClassName()
{
}

編集(太字):

オブジェクトがスコープ外に移動され、ガベージ コレクターによって整理されたときに呼び出されますが、これは決定論的ではなく、特定の時点で発生するとは限りません。これをファイナライザーと呼びます。ファイナライザーを持つすべてのオブジェクトは、ファイナライズ メソッドが呼び出されるガベージ コレクターによって特別なファイナライズ キューに入れられます (したがって、空のファイナライザーを宣言すると、技術的にはパフォーマンス ヒットになります)。

フレームワーク ガイドラインに従って「受け入れられた」破棄パターンは、アンマネージド リソースでは次のとおりです。

    public class DisposableFinalisableClass : IDisposable
    {
        ~DisposableFinalisableClass()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

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

            // tidy unmanaged resources
        }
    }

したがって、上記は、誰かが Dispose を呼び出すと、管理されていないリソースが片付けられることを意味します。ただし、誰かが Dispose を呼び出すのを忘れた場合、または Dispose が呼び出されるのを妨げる例外が発生した場合でも、アンマネージ リソースは片付けられますが、GC が汚れたミットを取得したとき (アプリケーションの終了や予期せぬ終了を含む) だけです。 )。

于 2008-09-06T16:16:40.180 に答える
2

@喧嘩

オブジェクトがスコープ外に移動され、ガベージ コレクターによって整理されると、If が呼び出されます。

このステートメントは誤解を招きやすく、私の読み方は間違っています。ファイナライザーがいつ呼び出されるかという保証はまったくありません。billpg がファイナライザーを実装する必要があることは絶対に正しいです。ただし、オブジェクトがスコープ外になると、自動的には呼び出されません。 証拠、ファイナライズ操作の下の最初の箇条書きには、次の制限があります。

実際、Microsoft は Chris Sells に、ガベージ コレクションLinkの代わりに参照カウントを使用する .NET の実装を作成する許可を与えました。結局のところ、かなりのパフォーマンス ヒットがありました。

于 2008-09-06T16:34:56.593 に答える
0

これはプログラマーが C++ でdeleteを使用するのを忘れているのと同じです。

また、心配しているリソースがメモリだけである場合は、IDisposable を使用する必要はありません。フレームワークはそれを独自に処理します。IDisposable は、データベース接続、ファイルストリーム、ソケットなどの管理されていないリソース専用です。

于 2008-09-06T16:27:39.840 に答える
0

より良い設計は、このクラスが高価なリソースを破棄する前に独自に解放するようにすることです。

たとえば、データベース接続の場合は、実際のクラスが破棄されるずっと前に、必要なときにのみ接続し、すぐに解放します。

于 2008-09-06T16:48:04.347 に答える