14

使い捨てオブジェクトが .Net でどのように実装されているかについての意見はありますか? また、IDisposable クラスの実装の反復性をどのように解決しますか?

IDisposable 型は、本来あるべき一流の市民ではないと感じています。あまりにも多くのことが開発者のなすがままになっています。

具体的には、使い捨てのものを正しく実装し、適切に廃棄することを確実にするために、言語とツールのサポートが改善されるべきではなかったのではないかと思います。

たとえば C# では、使い捨てセマンティクスを実装する必要があるクラスを次のように宣言できたらどうなるでしょうか。

public class disposable MyDisposableThing
{
    ~MyDisposableThing()
    {
        // Dispose managed resources
    }
}

この場合、コンパイラは IDisposable インターフェイスの実装を簡単に生成できます。デストラクタ ~MyDisposableThing は、マネージド リソースを解放する実際の Dispose メソッドに変換できます。

中間の C# コードは次のようになります。

public class MyDisposableThing : IDisposable
{
    private void MyDisposableThingDestructor()
    {
        // Dispose my managed resources
    }

    ~MyDisposableThing()
    {
        DisposeMe(false);
    }

    public void Dispose()
    {
        DisposeMe(true);
        GC.SuppressFinalize(this);
    }

    private bool _disposed;
    private void DisposeMe(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // Call the userdefined "destructor" 
                MyDisposableThingDestructor();
            }
        }
        _disposed = true;
    }
}

これにより、コードがよりクリーンになり、ボイラープレートの破棄コードが少なくなり、マネージド リソースを破棄する一貫した方法が得られます。IDisposable を手動で実装することは、エッジ ケースとアンマネージ リソースに対して引き続きサポートされます。

インスタンスが適切に破棄されるようにすることは、別の課題です。次のコードを検討してください。

private string ReadFile(string filename)
{
    var reader = new StreamReader();
    return reader.ReadToEnd(filename);
}

リーダー変数がメソッドのスコープを超えて存続することはありませんが、GC が破棄するまで待機する必要があります。この場合、コンパイラは、StreamReader オブジェクトが明示的に破棄されていないというエラーを発生させる可能性があります。このエラーは、開発者に using ステートメントでラップするように促します。

private string ReadFile(string filename)
{
    using (var reader = new StreamReader())
    {
        return reader.ReadToEnd(filename);
    }
}
4

7 に答える 7

24

よく言われる原則は、「言語の欠陥に対処するには設計パターンが必要である」というものです。これはその原則の例です。言語が提供しないため、使い捨てパターンが必要です。

プロパティのゲッターとセッター (ゲッター メソッドとセッター メソッドを持つパターンの標準化) やイベントで行ったように、「パターン」の世界から適切な C# 言語に処分可能性を高めることができた可能性があることに同意します。 (これにより、デリゲートを格納し、何か興味深いことが起こったときにそれを呼び出すという考え方が標準化されます。)

しかし、言語設計には費用がかかり、それに適用できる労力には限りがあります。したがって、私たちは、適切な言語に入れる最も有用で説得力のあるパターンを見つけようとします。そして、単に便利なだけでなく、実際に言語により多くの表現力を追加する方法を見つけようとしています. たとえば、LINQ は、データのフィルタリング、射影、結合、グループ化、および順序付けの概念を適切な言語に移行し、言語に多くの表現力を追加します。

これは確かに良いアイデアですが、基準を満たしていないと思います。これは便利だということには同意しますが、非常にリッチな新しいシナリオが可能になるわけではありません。

于 2009-05-28T02:39:49.200 に答える
5

個人的にIDisposableは、現在のバージョンの .NET での のサポートはかなりまともだと思います。キーワードの存在は、using私にとってそれを第一級の構造にしています。

一定量の定型コードが含まれていることは認めますが、新しい言語機能を保証するには不十分です。(自動実装されたプロパティは、導入を切望していた機能の良い例です。) あなたは、この「ボイラープレート」コードが必ずしも必要なものではないという重要なポイントを投稿で見逃していました。主に、管理されていないリソースをif (disposing)ブロックの外に配置する必要があります。

もちろん、デストラクタ ( ~MyDisposableThing) とパラメータDispose()のないメソッドは純粋にボイラープレートであり、言語キーワードのユーザーによって削除される可能性があります。コード行。

私は確かにあなたがここで主張していることを理解しており、ある程度同情しています. (あなたの提案が言語仕様の一部になったとしても、コーダーは文句を言わないでしょう。) ただし、とにかくコード行数がかなり限られている場合、.NET 開発チームを納得させることはほとんどありません。かなりコンテキスト固有です(したがって定型文ではありません)。

于 2009-05-28T00:32:30.017 に答える
4

IDisposableより良い言語サポートが必要であることには完全に同意します。これは、少し前の私の変種です。詳細は間違っている可能性がありますが、C++/CLI はこのための非常に優れたモデルとして機能します。残念ながら、私が C++/CLI で例を示すと、C# プログラマーを混乱させてしまいます。しかし、実装に関してはすでに「正しいこと」を行っています。C# で新しい構文が必要になるだけです。

最も単純な Windows フォーム アプリケーションでさえ、Disposeウィザードによって生成されるメソッドが含まれており、熟練していない変更に直面すると壊れやすくなります。1 つのコンポーネントが他の複数のコンポーネントを「所有」できるようにコンポーネントを組み合わせて構成するという考えは、非常に基本的なものでIDisposableあり、実際には避けられません。残念ながら、それを正しく実装する方法を説明するには、ほとんどの本で数ページかかるようです。

既存のusingステートメントは、クライアント側を処理します。より多くの言語サポートが必要なのは、実装側です。

クラスのフィールドには、そのクラスが「所有」するものへの参照と、所有していないものへの参照があります。そのため、フィールドを所有済みとしてマークできる必要があります。

また、ファイナライザーを自動的に生成するのは非常に悪い考えです。ほとんどの場合、クラスは を実装する他のオブジェクトを所有しますIDisposable。すべてのクラスがスレッド セーフであるわけではなく、スレッド セーフである必要もありません。それらがファイナライザーから呼び出された場合、それは別のスレッドで発生し、強制的にスレッドセーフになります。これはおそらく、IDisposable最も混乱を引き起こす領域の 1 つです。多くの人が本を読んIDisposable.

于 2009-07-26T20:04:26.640 に答える
2

これが古いスレッドであることは認識していますが、見落とされているものがあります。

C# と Java の両方の Dispose パターンは、決定論的デストラクタを持たない根本的な理由を打ち破ります。

MyObject A = new MyObject()
MyObject B = A;
A.Dispose();

Bさんの現在の状態は?B の所有者が B の処分を本当に望んでいない場合はどうなるでしょうか。保持しているオブジェクトのすべての参照を追跡する必要がある C++ でも同じ問題が発生しています。

IDisposableusing()例外が発生した場合のリソースのクリーンアップのコンテキスト内でのみ真に有効です。

これを行うための設計パターンはありますが、そうではありませんIDisposable

@Peterはい、Dipsosableパターンに欠陥があると主張しています。Disposable パターンが実装された場合、通常は、破棄されたオブジェクトを引き続き使用できるという考えで OS リソースを単に破棄することを意図したものではありません。Java の try{} finally{} または .NET の using() の外で Disposable パターンを使用すると、GC を使用する理由の 1 つを破ることができます。メモリがリークすると言っているのではありません。破棄されたオブジェクトへの参照を持つコードの他の部分を使用できるようになったと言っています。これで、各呼び出しの前にオブジェクトが破棄されているかどうかを確認するか、少なくとも ObjectDisposedException をキャッチするかどうかを確認する責任が開発者に戻ります。

ばかげた例を見てみましょう:

FileStream stream = new FileStream (@"c:\mylog.txt");
logger.setStream (stream);

.Dispose() を呼び出すのは誰ですか? ロガーがファイル ストリームの所有権を取得していることは明らかではありません。ストリームが開発者以外の別の場所で作成され、ログ ストリームとして設定されることがわかっているとします。

1行追加すると、ロガーが壊れます

using (FileStream stream = new FileStream (@"c:\mylog.txt"))
    { logger.setStream (stream); }

また

FileStream stream = new FileStream (@"c:\mylog.txt");
logger.setStream (stream);
stream.Dispose();

Disposable パターンは、リソースのカウントを参照しません。開発者は、オブジェクトの所有者とクリーンアップの責任者を意識する必要があります。実際の問題は、Dispose() が呼び出されると、通常の動作では、オブジェクト全体が無効になり、使用できなくなることです。

于 2011-02-18T17:32:02.190 に答える
0

IMHO、.net言語には、iDisposableの処理に大きな欠点があります。これは、例外をスローする初期化子を処理する優れた方法がないことです。構築中のオブジェクトまたはその中の使い捨てオブジェクトのコピーを「リーク」しない限り、初期化子がスローする前に(初期化子または基本レベルのコンストラクターで)作成されたiDisposableオブジェクトをクリーンアップする方法はありません。

そのために私が見たい2つの機能:

  1. コンストラクターから例外がスローされた場合に特定のメソッドが呼び出されるようにするクラス宣言。
  2. 特別なプライベートメソッドまたはキーワードがオブジェクトで使用されている場合、フィールドに.Disposeが呼び出される必要があることを示すフィールド宣言。

ところで、私はまた、メソッドが基礎となる構造を変更することを示す構造メソッドで利用可能な宣言を見たいと思います。このようなメソッドを構造体の右辺値で使用することは禁止されており、構造体のプロパティでこのようなメソッドを使用すると、読み取り-変更-書き込みシーケンスが生成されます。

于 2010-09-30T23:09:41.337 に答える
-1

管理されたメモリと管理されていないメモリの違いを理解する必要があります。簡単に言えば、C++ スタイルのデストラクタは、オブジェクトがガベージ コレクションされるタイミングが保証されていないため、C# のマネージの世界では機能しません。したがって、デストラクタが呼び出されるタイミングがわからず、事態が非常に予測不能になります。

C++ とは対照的に、クラスがスコープ外になるとすぐにデストラクタが呼び出されるため、いつ呼び出されるかを保証できます。

これが、C# がデストラクタを持てない理由です。

于 2009-05-28T00:33:05.883 に答える