IDisposableを正しく実装する


145

私のクラスでは、IDisposableを次のように実装します。

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int UserID)
    {
        id = UserID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

    public void Dispose()
    {
        // Clear all property values that maybe have been set
        // when the class was instantiated
        id = 0;
        name = String.Empty;
        pass = String.Empty;
    }
}

VS2012では、私のコード分析でIDisposableを正しく実装するように言われていますが、ここで何が間違っているのかわかりません。
正確なテキストは次のとおりです。

CA1063 IDisposableを正しく実装する 'User'でDispose(bool)のオーバーライド可能な実装を提供するか、型をシール済みとしてマークします。Dispose(false)を呼び出すと、ネイティブリソースのみがクリーンアップされます。Dispose(true)を呼び出すと、マネージリソースとネイティブリソースの両方がクリーンアップされます。stman User.cs 10

参考:CA1063:IDisposableを正しく実装する

私はこのページを読みましたが、ここで何をする必要があるのか​​本当にわかりません。

問題が何であるか、および/またはIDisposableをどのように実装する必要があるかについて、より多くの言葉で誰かが説明できれば、それは本当に役立ちます!


1
内部のコードはすべてDisposeですか?
Claudio Redi

42
Dispose()メソッドを実装して、クラスのメンバーのいずれかでDispose()メソッドを呼び出す必要があります。それらのメンバーは誰も持っていません。したがって、IDisposableを実装しないでください。プロパティ値をリセットしても意味がありません。
Hans Passant

13
IDispoable破棄するアンマネージリソースがある場合にのみ実装する必要があります(これにはSqlConnection、ラップされたアンマネージリソース(、、FileStreamなど)が含まれます。ここにあるようなマネージリソースのみがある場合実装しないでくださいIDisposable。これはIMO、コード分析の主要な問題それは他愛ないルールを確認するには非常に良いですが、。ない概念的なエラーをチェックするのが得意。
ジェイソン・

51
一部の人々は、概念を明らかに誤解している人を手助けしようとするよりも、この質問に反対票を投じて閉じた方がよいと考えるのはかなり気が動転しています。残念だ。
Ortund 2013

2
ですから、反対票を投じたり、反対票を投じたりせず、投稿をゼロのままにして、役立つポインタで質問を閉じてください。
tjmoore

回答:


113

これは正しい実装ですが、投稿したコードで破棄する必要があるものは何もありません。次のIDisposable場合にのみ実装する必要があります。

  1. 管理されていないリソースがあります
  2. あなたはそれ自体が使い捨て可能なものへの言及を握っています。

あなたが投稿したコードの何も処分する必要はありません。

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int userID)
    {
        id = userID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources if there are any.
    }
}

2
C#で書き始めたusing(){ }ときに、可能な限り利用するのが最善であると言われましたが、そのためにはIDisposableを実装する必要があるため、一般的に、次の使用法を使用してクラスにアクセスすることを好みます。1つまたは2つの関数のクラスだけが必要な場合
Ortund

62
@Ortundあなたは誤解しました。クラスがIDisposableを実装する場合はusingブロックを使用するのが最善です。クラスを使い捨てにする必要がない場合は、実装しないでください。目的はありません。
Daniel Mann

5
@DanielMannただし、usingブロックのセマンティクスは、IDisposableインターフェースだけでなく魅力的である傾向があります。IDisposableスコーピングのみを目的とした虐待がいくつかあったと思います。
トーマス

1
アンマネージリソースを解放する必要がある場合の補足として、Dispose(false)を呼び出すFinalizerを含める必要があります。これにより、ガベージコレクション(Disposeがまだ呼び出されていない場合)時にGCがFinalizerを呼び出し、アンマネージを適切に解放できます。リソース。
mariozski、

4
実装にファイナライザがなければ、呼び出しGC.SuppressFinalize(this);は無意味です。@mariozskiは、ファイナライザを指摘したようにするために役立つだろう確実にそのDisposeクラスが内部で使用されていない場合は、すべてで呼ばれるusingブロック。
Haymo Kutschbach、2016年

57

まず、stringsとints を「クリーンアップ」する必要はありません。これらはガベージコレクターによって自動的に処理されます。クリーンアップする必要があるのDisposeは、を実装するアンマネージリソースまたはマネージリソースですIDisposable

ただし、これが単なる学習課題であると想定すると、実装するための推奨される方法IDisposableは、「セーフティキャッチ」を追加して、リソースが2回破棄されないようにすることです。

public void Dispose()
{
    Dispose(true);

    // Use SupressFinalize in case a subclass 
    // of this type implements a finalizer.
    GC.SuppressFinalize(this);   
}
protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing) 
        {
            // Clear all property values that maybe have been set
            // when the class was instantiated
            id = 0;
            name = String.Empty;
            pass = String.Empty;
        }

        // Indicate that the instance has been disposed.
        _disposed = true;   
    }
}

3
+1、クリーンアップコードが1回だけ実行されることを確認するフラグを持つことは、プロパティをnullなどに設定するよりも優れています(特にreadonlyセマンティクスに干渉するため)
Thomas

+1は、ユーザーコードを使用して(自動的にクリーンアップされますが)、そこに何があるかを明確にするために使用します。また、ここでは他の多くの人と同じように学びながら、塩辛い船乗りではなく、小さな間違いを犯したことで彼を叩きました。
Murphybro2

42

次の例は、IDisposableインターフェースを実装するための一般的なベストプラクティスを示しています。参照

クラスにアンマネージリソースがある場合にのみ、デストラクタ(ファイナライザ)が必要になることに注意してください。また、デストラクタを追加する場合は、DisposeFinalizationを抑制する必要があります。そうしないと、オブジェクトが2つのガベージサイクルの間メモリに常駐します(注:Finalizationのしくみをお読みください)。以下の例は、上記のすべてを詳しく説明したものです。

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

14

IDisposableガベージコレクターによって自動的にクリーンアップされないアンマネージリソースをクリーンアップする手段を提供するために存在します。

「クリーンアップ」しているすべてのリソースはマネージリソースであり、そのため、Disposeメソッドは何も実行しません。あなたのクラスはまったく実装すべきではありませんIDisposable。ガベージコレクターは、これらのフィールドすべてを適切に処理します。


1
これに同意します-実際には不要なときにすべてを破棄するという考えがあります。Disposeは、クリーンアップするアンマネージリソースがある場合にのみ使用してください。
Chandramouleswaran Ravichandra 2014年

4
厳密には正しくありません、Disposeメソッドはまた、「IDisposableインターを実装して管理するリソース」を処分することができます
マットWilko

@MattWilko 別のリソースがアンマネージリソースを破棄できるため、アンマネージリソースを間接的に破棄できます。ここでは、別の管理対象リソースを介した非管理対象リソースへの間接参照もありません。
サービー

@MattWilko Disposeは、IDesposableを実装した管理対象リソースで自動的に呼び出されます
パンキーシャルマ

@pankysharmaいいえ、できません。手動で呼び出す必要があります。それはそれの全体のポイントです。あなたはできません、あなただけの人が知っている、それが自動的に呼び出されますと仮定はずそれを手動で呼び出すには、人々は、makeのミスをして、忘れています。
Servy

14

次のような使い捨てパターンを使用する必要があります。

private bool _disposed = false;

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            // Dispose any managed objects
            // ...
        }

        // Now disposed of any unmanaged objects
        // ...

        _disposed = true;
    }
}

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

// Destructor
~YourClassName()
{
    Dispose(false);
}

1
デストラクタでもGC.SuppressFinalize(this)を呼び出すほうが賢明ではないでしょうか?それ以外の場合、オブジェクト自体は次のGCで再利用されます
Sudhanshu Mishra

2
@dotnetguy:GCの実行時にオブジェクトデストラクタが呼び出されます。したがって、2回呼び出すことはできません。こちらを参照してください:msdn.microsoft.com/en-us/library/ms244737.aspx
schoetbi

1
それでは、定型コードの一部を「パターン」と呼んでいますか?
チェル2015

4
@rdhsいいえ、違います。MSDNには、それが述べIS -ここでは、パターン「廃棄パターン」msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspxので、前にダウン投票多分Googleの少し?
Belogix 2015

2
Microsoftもあなたの投稿も、パターンがこのように見えるべきである理由を明確に述べていません。一般的に、それは定型文でさえありません、それは単に不必要です-によって置き換えられますSafeHandle(およびサブタイプ)。管理されたリソースの場合、適切な処分を実装すると、はるかに簡単になります。void Dispose()メソッドの単純な実装にコードをトリミングできます。
BatteryBackupUnit 2018

10

あなたは行う必要がないUserクラスビーイングをIDisposableクラスがあるため取得しない任意の非管理対象リソース(ファイル、データベース接続など)。通常、クラスにIDisposableは少なくとも1つのIDisposableフィールドまたはプロパティ、あるいはその両方があるかのようにマークを付け ます。を実装する場合はIDisposable、Microsoftの一般的なスキームに基づいて実装することをお勧めします。

public class User: IDisposable {
  ...
  protected virtual void Dispose(Boolean disposing) {
    if (disposing) {
      // There's no need to set zero empty values to fields 
      // id = 0;
      // name = String.Empty;
      // pass = String.Empty;

      //TODO: free your true resources here (usually IDisposable fields)
    }
  }

  public void Dispose() {
    Dispose(true);

    GC.SuppressFinalize(this);
  } 
}

それがあります通常の場合。しかし一方で、usingコンストラクトは、C ++スマートポインターのようなもの、つまり、usingブロックがどのように終了されても以前の状態を復元するオブジェクトを書き込む可能性を開きます。私がこれを行う唯一の方法は、そのようなオブジェクトにIDisposableを実装させることです。このような限界的な使用例では、コンパイラの警告を無視できるようです。
Papa Smurf、2018

3

Idisposableは、確定的な(確認された)ガベージコレクションが必要なときに実装されます。

class Users : IDisposable
    {
        ~Users()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
            // This method will remove current object from garbage collector's queue 
            // and stop calling finilize method twice 
        }    

        public void Dispose(bool disposer)
        {
            if (disposer)
            {
                // dispose the managed objects
            }
            // dispose the unmanaged objects
        }
    }

Usersクラスを作成して使用するときは、「using」ブロックを使用して、disposeメソッドを明示的に呼び出さないようにします。

using (Users _user = new Users())
            {
                // do user related work
            }

usingブロックの最後に作成されたUsersオブジェクトは、disposeメソッドの暗黙的な呼び出しによって破棄されます。


2

本当にアンチパターンであるMicrosoft Disposeパターンの例がたくさんあります。多くの人が問題のコードを指摘したように、IDisposableはまったく必要ありません。ただし、実装する場合は、Microsoftパターンを使用しないでください。より良い答えは、この記事の提案に従うことです:

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

おそらく役立つ他の唯一のものは、そのコード分析警告を抑制することです... https://docs.microsoft.com/en-us/visualstudio/code-quality/in-source-suppression-overview?view=vs- 2017年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.