web-dev-qa-db-fra.com

Quel est l'intérêt de remplacer Dispose (suppression des booléens) dans .NET?

Si j'écris une classe en C # qui implémente IDisposable, pourquoi ne me suffit-il pas de simplement implémenter

public void Dispose(){ ... } 

gérer la libération de ressources non gérées?

Est

protected virtual void Dispose(bool disposing){ ... }

toujours nécessaire, parfois nécessaire, ou autre chose?

41
Mark Carpenter

Ce n'est pas strictement nécessaire. Il fait partie du modèle jetable recommandé. Si vous n'avez pas lu la section Framework Design Guidelines à ce sujet (9.3 dans la première édition, ne disposez pas de la deuxième édition, désolé), alors vous devriez le faire. Essayez ce lien .

C'est utile pour faire la distinction entre le nettoyage jetable et le garbage-collection-is-trashing-me finalisable.

Vous n'êtes pas obligé de le faire de cette façon, mais vous devriez lire et comprendre pourquoi cela est recommandé avant de le considérer comme inutile.

31
Hamish Smith

Le modèle complet comprenant un finaliseur, l'introduction d'une nouvelle méthode virtuelle et le "scellement" de la méthode d'élimination originale est un objectif très général, couvrant toutes les bases.

Sauf si vous avez des poignées directes sur des ressources non gérées (qui devraient être presquejamais ) vous ne le faites pas ' t besoin d'un finaliseur.

Si vous scellez votre classe (et mes vues sur le scellement des classes dans la mesure du possible sont probablement bien connues maintenant - conception pour l'héritage ou l'interdire ) il n'y a aucun intérêt à introduire une méthode virtuelle.

Je ne me souviens pas de la dernière fois que j'ai implémenté IDisposable de manière "compliquée" plutôt que de le faire de la manière la plus évidente, par ex.

public void Dispose()
{
    somethingElse.Dispose();
}

Une chose à noter est que si vous optez pour un code vraiment robuste, vous devez vous assurer de ne pas essayer de faire quoi que ce soit après vous ' ont été supprimés, et lancez ObjectDisposedException le cas échéant. C'est un bon conseil pour les bibliothèques de classes qui seront utilisées par les développeurs du monde entier, mais c'est beaucoup de travail pour très peu de gain s'il s'agit simplement d'une classe utilisée dans votre propre espace de travail.

43
Jon Skeet

Il y a un peu de biais dans la documentation MSFT concernant le modèle jetable. Il y a deux raisons pour lesquelles vous devez implémenter IDisposable:

  1. Vous avez des champs d'un type qui implémente IDisposable
  2. Vous avez un finaliseur.

Le cas 1 est assez courant dans la plupart des codes. Le cas 2 est assez courant dans le code que Microsoft écrit, ce sont eux qui ont écrit les wrappers gérés autour des ressources non gérées, ceux qui doivent être finalisés. Mais cela devrait être très rare dans votre code. Après tout, vous avez toutes ces classes Nice .NET pour faire le sale boulot à votre place. Il vous suffit d'appeler leurs méthodes Dispose ().

Seul le cas 2 nécessite le patron jetable. Microsoft a besoin de beaucoup l'utiliser. Vous aurez juste besoin du simple Dispose () la plupart du temps.

14
Hans Passant

En plus des autres bonnes réponses, vous voudrez peut-être consulter ces articles:

5
Hosam Aly

La méthode supplémentaire avec l'élimination des booléens est issue d'une directive de conception de cadre quelque part. Il s'agit simplement d'un modèle permettant à votre classe de faire en sorte que la méthode dispose puisse être appelée plusieurs fois sans lever d'exception. Ce n'est pas absolument nécessaire. Techniquement, vous pouvez le faire dans la méthode de disposition.

3
Bob

Juste pour développer ce que les autres ont dit: ce n'est pas seulement que vous n'avez pas besoin du 'complexe disposer', c'est que vous en fait pas envie , pour des raisons de performances.

Si vous optez pour la route `` complexe disposer '', implémentez un finaliseur, puis oubliez de supprimer explicitement votre objet, votre objet (et tout ce qu'il référence) survivra à une génération GC supplémentaire avant d'être vraiment éliminé (car il doit en rester un plus de temps pour que le CLR appelle le finaliseur). Cela provoque simplement plus de pression sur la mémoire dont vous n'avez pas besoin. De plus, appeler le finaliseur sur tout un tas d'objets a un coût non négligeable.

Évitez donc, sauf si vous (ou vos types dérivés) avez des ressources non gérées.

Oh, et pendant que nous sommes dans la zone: les méthodes de votre classe qui gèrent les événements des autres doivent être "sûres" face à une invocation après que votre classe a été supprimée. Le plus simple est d'effectuer simplement un no-op si la classe est supprimée. Voir http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx

1
piers7

Si une classe implémente IDisposable.Dispose() et qu'une classe dérivée doit ajouter une logique supplémentaire, cette classe doit exposer une sorte de méthode Dispose que la classe dérivée peut enchaîner. Comme certaines classes peuvent implémenter IDisposable.Dispose() sans avoir de méthode publique Dispose(), il est utile d'avoir une méthode virtuelle qui sera protected dans toutes les implémentations de IDisposable qu'ils aient une méthode publique Dispose ou non. Dans la plupart des cas, l'argument bool n'est pas vraiment significatif mais doit être considéré comme un argument factice pour que la protected virtual Dispose(bool) ait une signature différente de celle de peut-être-ou-peut-être- not-public Dispose().

Les classes qui n'utilisent pas de protected virtual Dispose(bool) nécessiteront des classes dérivées pour gérer leur logique de nettoyage d'une manière différente de la convention. Certains langages comme C++/CLI qui ne sont équipés que pour étendre les implémentations IDisposable qui suivent cette convention peuvent être incapables de dériver des classes à partir d'implémentations non standard.

1
supercat

Une chose qu'il vous donne est la possibilité de faire du travail dans Dispose () sans rapport avec la finalisation, tout en nettoyant les ressources non gérées.

Faire n'importe quoi à un objet géré autre que "vous-même" dans un finaliseur est extrêmement ... imprévisible. Cela est dû en grande partie au fait que vos finaliseurs seront appelés lors de l'arrêt de l'étape 2 de votre AppDomain d'une manière non déterministe - donc lorsque votre finaliseur est appelé, il est extrêmement probable que les objets auxquels vous avez encore des références aient déjà été finalisé.

La distribution des appels Dispose et finalizer à la même méthode vous permet de partager votre code d'arrêt, tandis que le paramètre booléen vous permet d'ignorer le nettoyage géré si vous en avez.

De plus, la virtualité de la méthode permet aux héritiers d'ajouter facilement leur propre code de nettoyage, avec moins de risque de ne pas appeler le vôtre par inadvertance.

1
kyoryu