web-dev-qa-db-fra.com

Implémenter correctement IDisposable

Dans mes classes, j'implémente IDisposable comme suit:

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;
    }
}

Dans VS2012, mon analyse de code indique qu'il faut implémenter correctement IDisposable, mais je ne suis pas sûr de ce que j'ai mal fait ici.
Le texte exact est le suivant:

CA1063 Implémenter correctement IDisposable Fournissez une implémentation remplaçable de Dispose (bool) sur 'User' ou marquez le type comme étant scellé. Un appel à Dispose (false) ne doit nettoyer que les ressources natives. Un appel à Dispose (true) doit nettoyer les ressources gérées et les ressources natives. stman User.cs 10

Pour référence: CA1063: Implémenter correctement IDisposable

J'ai lu cette page, mais j'ai bien peur de ne pas comprendre ce qui doit être fait ici.

Si quelqu'un peut expliquer en termes plus détaillés le problème et/ou la manière dont IDisposable doit être mis en œuvre, cela aidera vraiment!

119
Ortund

Ce serait la bonne implémentation, même si je ne vois pas ce dont vous avez besoin de disposer dans le code que vous avez posté. Vous devez uniquement implémenter IDisposable lorsque:

  1. Vous avez des ressources non gérées
  2. Vous conservez des références d'éléments qui sont eux-mêmes jetables.

Rien dans le code que vous avez posté n'a besoin d'être éliminé.

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

}
94
Daniel Mann

Tout d'abord, vous n'avez pas besoin de "nettoyer" strings et ints - elles seront traitées automatiquement par le ramasse-miettes. Les seules choses à nettoyer dans Dispose sont les ressources non gérées ou les ressources gérées qui implémentent IDisposable.

Cependant, en supposant qu'il ne s'agisse que d'un exercice d'apprentissage, la méthode recommandée pour mettre en œuvre IDisposable consiste à ajouter un "verrou de sécurité" pour garantir que les ressources ne sont pas éliminées deux fois:

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;   
    }
}
52
D Stanley

L'exemple suivant montre la meilleure pratique générale pour implémenter l'interface IDisposable. Référence

N'oubliez pas que vous n'avez besoin d'un destructeur (finaliseur) que si vous avez des ressources non gérées dans votre classe. Et si vous ajoutez un destructeur, vous devez supprimer la finalisation dans Dispose, sinon vos objets resteront en mémoire pendant deux cycles de mémoire (Remarque: Lisez comment fonctionne la finalisation ). L'exemple ci-dessous donne tous les détails ci-dessus.

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

IDisposable existe pour vous permettre de nettoyer unmanaged des ressources qui ne seront pas nettoyées automatiquement par le récupérateur de place.

Toutes les ressources que vous "nettoyez" sont des ressources gérées, et votre méthode Dispose n'effectue donc rien. Votre classe ne devrait pas implémenter IDisposable du tout. Le ramasse-miettes s’occupe de tous ces champs tout seul.

13
Servy

Vous devez utiliser le motif Jetable comme ceci:

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

Vous n'avez pas besoin de faire votre classe User étant IDisposable puisque la classe n'acquiert aucune ressource non gérée (fichier, connexion à la base de données, etc.) En général, nous marquons les classes comme IDisposable si elles ont au moins un champ IDisposable ou/et une propriété. Lors de la mise en œuvre de IDisposable, mieux vaut le mettre en conformité avec le schéma typique de 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);
  } 
}
7
Dmitry Bychenko

Idisposable est implémenté chaque fois que vous souhaitez une récupération de place déterministe (confirmée).

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
        }
    }

Lors de la création et de l'utilisation de la classe Users, utilisez le bloc "using" pour éviter d'appeler explicitement la méthode dispose: 

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

la fin de l'objet Utilisateurs créé dans le bloc using sera supprimée par invocation implicite de la méthode dispose.

3
S.Roshanth

IDisposable implémenté pour garbage collection.L'interface IDisposable implant dispose d'une méthode de suppression qu'elle a implicitement appelée à partir du thread du finaliseur.

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
Sunil Dhappadhule

Je vois beaucoup d'exemples du modèle Microsoft Dispose, qui est en réalité un anti-modèle. Comme beaucoup l'ont souligné, le code de la question n'exige pas du tout IDisposable. Mais si vous voulez l’implémenter, n’utilisez pas le modèle Microsoft. Une meilleure réponse serait de suivre les suggestions de cet article:

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

La seule autre chose qui pourrait être utile est de supprimer l'avertissement d'analyse de code ... https://docs.Microsoft.com/en-us/visualstudio/code-quality/in-source-suppression-overview?view= vs-2017

0
MikeJ