web-dev-qa-db-ja.com

IDisposableを正しく実装する

私のクラスでは、次のように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を正しく実装します。「ユーザー」にDispose(bool)のオーバーライド可能な実装を提供するか、タイプを封印済みとしてマークします。 Dispose(false)の呼び出しは、ネイティブリソースのみをクリーンアップする必要があります。 Dispose(true)を呼び出すと、マネージリソースとネイティブリソースの両方がクリーンアップされます。 stman User.cs 10

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

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

誰が問題が何であるか、および/またはIDisposableをどのように実装する必要があるかをより詳細な用語で説明できる場合、それは本当に役立ちます!

123
Ortund

これは正しい実装になりますが、投稿したコードにあなたが処分する必要があるものは見当たりません。次の場合にのみ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.
    }

}
95
Daniel Mann

まず、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;   
    }
}
53
D Stanley

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

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

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.
    }
}
37
CharithJ

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

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

13
Servy

次のようにDisposable Patternを使用する必要があります。

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);
}
12
Belogix

クラス取得しない管理されていないリソース(ファイル、データベース接続など)なので、UserクラスをIDisposableにする必要はありません。通常、クラスに少なくとも1つのIDisposableフィールドまたはプロパティが含まれている場合は、クラスを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);
  } 
}
8
Dmitry Bychenko

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メソッドの暗黙的な呼び出しによって破棄されます。

3
S.Roshanth

ガベージコレクション用に実装されたIDisposable。IDisposableインターフェイスは、ファイナライザスレッドから暗黙的に呼び出されるdisposeメソッドを実装します。

internal class ConnectionConfiguration : IDisposable
{
    private static volatile IConnection _masterconnection;
    private static readonly object ConnectionLock = new object();
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (!disposing)
        {
            return;
        }
        if (_masterconnection == null)
        {
            return;
        }
        lock (ConnectionLock)
        {
            if (_masterconnection == null)
            {
                return;
            }
            _masterconnection?.Dispose();//double check
            _masterconnection = null;
        }
    }
}
0

本当にアンチパターンである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

0
MikeJ