94 votes

Comment vérifier si un objet a été éliminé en C# ?

Duplicata possible :
Comment savoir si une référence d'objet IDisposable est éliminée ?

Existe-t-il une méthode pour vérifier si l'objet a été disposé différemment alors

try
{
    myObj.CallRandomMethod();
} catch (ObjectDisposedException e)
{
    // now I know object has been disposed
}

Dans mon cas, j'utilise TcpClient qui a Close() qui dispose de l'objet et cela peut arriver dans un morceau de code dont je n'ai pas le contrôle. Dans ce cas, j'aimerais avoir une meilleure solution que d'attraper une exception.

3 votes

0 votes

Je savais que ce problème en général est trop commun pour ne pas avoir de questions et réponses sur stackoverflow mais j'ai omis de le rechercher.

54voto

Luca Points 5487

La solution fiable consiste à attraper l'exception ObjectDisposedException.

La solution d'écrire votre implémentation surchargée de la méthode Dispose ne fonctionne pas, puisqu'il y a une race condition entre le thread qui appelle la méthode Dispose et celui qui accède à l'objet : après avoir vérifié l'hypothétique propriété IsDisposed , l'objet pourrait être réellement disposé, en levant tout de même l'exception.

Une autre approche pourrait être d'exposer un événement hypothétique Disposed (comme este ), qui est utilisé pour notifier l'objet jeté à tous les objets intéressés, mais cela peut être difficile à planifier en fonction de la conception du logiciel.

8 votes

Les exceptions sont censées gérer les exceptions, et non faire partie de l'infrastructure du code. Les utiliser est paresseux et n'est pas ce pour quoi elles sont là, c'est pourquoi le message original demandait explicitement une alternative fiable.

44voto

Hans Passant Points 475940

Une bonne méthode consiste à dériver de TcpClient et à surcharger la méthode Disposing(bool) :

class MyClient : TcpClient {
    public bool IsDead { get; set; }
    protected override void Dispose(bool disposing) {
        IsDead = true;
        base.Dispose(disposing);
    }
}

Ce qui ne fonctionnera pas si l'autre code a créé l'instance. Vous devrez alors faire quelque chose de désespéré, comme utiliser Reflection pour obtenir la valeur du membre privé m_CleanedUp. Ou bien attraper l'exception.

Franchement, rien de tout cela n'est susceptible d'aboutir à une très bonne fin. Vous avez vraiment a fait veulent écrire sur le port TCP. Mais vous ne le ferez pas, ce code bogué que vous ne pouvez pas contrôler est maintenant sous le contrôle de votre code. Vous avez augmenté l'impact du bug. Parler au propriétaire de ce code et trouver une solution est de loin la meilleure solution.

EDIT : Un exemple de réflexion :

using System.Reflection;
public static bool SocketIsDisposed(Socket s)
{
   BindingFlags bfIsDisposed = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty;
   // Retrieve a FieldInfo instance corresponding to the field
   PropertyInfo field = s.GetType().GetProperty("CleanedUp", bfIsDisposed);
   // Retrieve the value of the field, and cast as necessary
   return (bool)field.GetValue(s, null);
}

2 votes

Qu'en est-il des classes dans un fichier dll ? !

19voto

Ryan Brunner Points 8983

Si vous n'êtes pas sûr que l'objet a été éliminé ou non, vous devez appeler la fonction Dispose elle-même plutôt que des méthodes telles que Close . Bien que le framework ne garantisse pas que la méthode Dispose doit s'exécuter sans exception même si l'objet a déjà été éliminé, c'est un modèle commun et, à ma connaissance, il est implémenté sur tous les objets jetables du framework.

Le modèle typique pour Dispose selon Microsoft :

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 you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    if (!_disposed)
    {
        if (disposing) {
            if (_resource != null)
                _resource.Dispose();
                Console.WriteLine("Object disposed.");
        }

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

Remarquez le contrôle sur _disposed . Si vous deviez appeler un Dispose mettant en œuvre ce modèle, vous pouvez appeler Dispose autant de fois que vous le souhaitez sans rencontrer d'exceptions.

2 votes

Ce n'est pas utile. Le député est privé.

0 votes

Désolé, j'ai peut-être mal interprété votre question. Cherchez-vous à détecter si un objet est éliminé pour d'autres raisons que "dois-je éliminer cet objet" ? Si oui, pourquoi ? Le fait qu'un code inconnu puisse potentiellement disposer d'objets semble être une conception non fiable.

4 votes

Bien que le cadre de travail lui-même ne donne aucune garantie, la documentation relative à IDisposable dit ceci : "Si l'objet Dispose est appelée plus d'une fois, l'objet doit ignorer tous les appels après le premier. L'objet ne doit pas lever une exception si sa méthode Dispose est appelée plusieurs fois. Les méthodes d'instance autres que Dispose peut lancer un ObjectDisposedException lorsque les ressources sont déjà disposées." msdn.microsoft.com/fr/us/library/

-1voto

Boris Modylevsky Points 910

La meilleure pratique consiste à l'implémenter vous-même en utilisant un champ booléen local : http://www.niedermann.dk/2009/06/18/BestPracticeDisposePatternC.aspx

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X