Y a-t-il un avantage à implémenter IDisposable sur des classes qui n’ont pas de ressources?

En C #, si une classe, telle qu’une classe de gestionnaire, n’a pas de ressources, y a-t-il un avantage à le faire : IDisposable ?

Exemple simple:

 public interface IBoxManager { int addBox(Box b); } public class BoxManager : IBoxManager { public int addBox(Box b) { using(dataContext db = new dataContext()){ db.Boxes.add(b); db.SaveChanges(); } return b.id; } } 

Y aura-t-il un avantage dans l’utilisation de la mémoire lors de l’utilisation de BoxManager si elle implémente également IDisposable? public class BoxManager : IBoxManager , IDisposable

Par exemple:

 BoxManager bm = new BoxManager(); bm.add(myBox); bm.dispose();//is there benefit to doing this? 

Il n’y a que 2 raisons pour implémenter IDisposable sur un type

  • Le type contient des ressources natives qui doivent être libérées lorsque le type n’est plus utilisé
  • Le type contient des champs de type IDisposable

Si aucun de ces éléments n’est vrai, IDisposable pas IDisposable

MODIFIER

Plusieurs personnes ont mentionné que IDisposable est un bon moyen de mettre en œuvre des opérations de début / fin ou de réservation. Bien que ce ne soit pas l’intention originale d’ IDisposable elle fournit un motif très agréable.

 class Operation { class Helper : IDisposable { internal Operation Operation; public void Dispose() { Operation.EndOperation(); } } public IDisposable BeginOperation() { ... return new Helper() { Operation = this }; } private void EndOperation() { ... } } 

Note: Une autre manière intéressante d’implémenter ce modèle est avec lambdas. Au lieu de remettre un IDisposable à l’utilisateur et en espérant qu’il n’oublie pas d’appeler Dispose , demandez-lui de vous donner un lambda dans lequel il peut exécuter l’opération et fermer l’opération.

 public void BeginOperation(Action action) { BeginOperationCore(); try { action(); } finally { EndOperation(); } } 

Il n’y aura pas de différence entre la version jetable et la version non jetable si vous n’utilisez pas explicitement la méthode Dispose() .

Bien que l’implémentation d’IDisposable ne soit pas bénéfique pour votre code, je ne suis pas d’accord avec les autres opinions ici, qui affirment que IDisposable est uniquement destiné à (directement ou indirectement) libérer des ressources natives. IDisposable peut être utilisé chaque fois que l’object doit effectuer une tâche de nettoyage à la fin de sa durée de vie. C’est extrêmement utile avec l’ using .

Un exemple très populaire: dans ASP.NET MVC Html.BeginForm renvoie un IDisposable. Lors de la création, l’object ouvre la balise, lorsque Dispose est appelée, elle la ferme. Aucune ressource native impliquée, mais toujours une bonne utilisation de IDisposable.

Non, il n’y aura aucun avantage si vous ne faites pas quelque chose d’utile, comme la publication de ressources non managées que votre classe pourrait contenir dans la méthode Dispose .

Un sharepoint confusion majeur, qui peut ne pas être applicable dans votre cas, mais qui se pose souvent, est ce qui constitue exactement une “ressource”. Du sharepoint vue d’un object, une ressource non gérée est quelque chose qu’une entité externe ( ) “fait” ( *) en son nom, que cette entité externe continuera à faire – au désortingment des autres entités – jusqu’à ce qu’elle lui dise d’arrêter . Par exemple, si un object ouvre un fichier, la machine qui héberge le fichier peut accorder à cet object un access exclusif, empêchant tout autre utilisateur de l’univers de l’utiliser, à moins qu’il soit averti que l’access exclusif n’est plus nécessaire.

(*) qui pourrait être n’importe quoi, n’importe où; peut-être même pas sur le même ordinateur.

(**) ou une modification du comportement ou de l’état d’une entité extérieure

Si une entité externe fait quelque chose pour le compte d’un object abandonné et disparaît sans laisser au préalable à l’entité le savoir, ses services ne sont plus nécessaires, l’entité externe n’aura aucun moyen de savoir qu’elle doit cesser d’agir pour le compte de l’object n’existe plus. IDisposable fournit un moyen d’éviter ce problème en fournissant un moyen standard de notification des objects lorsque leurs services ne sont pas requirejs. Un object dont les services ne sont plus nécessaires n’aura généralement pas besoin de demander d’autres faveurs à d’autres entités et pourra donc demander que toute entité ayant agi en son nom cesse de le faire.

Pour permettre le cas où un object est abandonné sans IDisposable.Dispose() ait été appelé en premier, le système permet aux objects d’enregistrer une méthode de nettoyage “safeafe” appelée Finalize() . Parce que pour une raison quelconque, les créateurs de C # n’aiment pas le terme Finalize() , le langage nécessite l’utilisation d’une construction appelée “destructeur”, qui fait à peu près la même chose. Notez qu’en général, Finalize() masque plutôt que de résoudre les problèmes, et peut créer des problèmes qui lui sont propres. Il convient donc de l’utiliser avec une extrême prudence.

Une “ressource gérée” est généralement un nom donné à un object qui implémente IDisposable et qui implémente généralement, mais pas toujours, un finaliseur.

Non, s’il n’y a pas de ressources (gérées ou non gérées), il n’est pas nécessaire non plus d’ IDisposable .

Petite mise en garde: certaines personnes utilisent IDisposable pour nettoyer les gestionnaires d’événements ou les grands tampons de mémoire, mais

  • vous ne semblez pas utiliser ceux
  • c’est un schéma discutable de toute façon.

De mon expérience personnelle (confirmée par la discussion et d’autres articles ici), je dirais qu’il peut y avoir des situations où votre object utilise énormément d’événements, ou pas du tout, mais des abonnements fréquents et des désabonnements à l’événement, l’object n’est pas ramassé. Dans ce cas, dans Dispose je désabonne de tous les événements auxquels mon object a souscrit auparavant.

J’espère que cela t’aides.

IDisposable est également génial si vous souhaitez bénéficier de la syntaxe using () {} .

Dans un projet WPF avec ViewModels, je voulais pouvoir désactiver temporairement les événements NotifyPropertyChange . Pour être sûr que d’autres développeurs réactiveront les notifications, j’ai écrit un bout de code pour pouvoir écrire quelque chose comme:

 using (this.PreventNotifyPropertyChanges()) { // in this block NotifyPropertyChanged won't be called when changing a property value } 

La syntaxe est correcte et facilement lisible. Pour que cela fonctionne, il y a un peu de code à écrire. Vous aurez besoin d’un object et d’un compteur jetables simples.

 public class MyViewModel { private volatile int notifyPropertylocks = 0; // number of locks protected void NotifyPropertyChanged(ssortingng propertyName) { if (this.notifyPropertylocks == 0) { // check the counter this.NotifyPropertyChanged(...); } } protected IDisposable PreventNotifyPropertyChanges() { return new PropertyChangeLock(this); } public class PropertyChangeLock : IDisposable { MyViewModel vm; // creating this object will increment the lock counter public PropertyChangeLock(MyViewModel vm) { this.vm = vm; this.vm.notifyPropertylocks += 1; } // disposing this object will decrement the lock counter public void Dispose() { if (this.vm != null) { this.vm.notifyPropertylocks -= 1; this.vm = null; } } } } 

Il n’y a pas de ressources à disposer ici. Je voulais un code propre avec une sorte de syntaxe try / finally. Le mot-clé using a l’air mieux.

Y a-t-il un avantage à l’avoir: IDisposable?

Cela ne semble pas être le cas dans votre exemple spécifique: il y a une bonne raison d’implémenter IDisposable même si vous n’avez pas de champs IDisposable : vos descendants le pourraient.

C’est l’un des grands problèmes architecturaux d’ IDisposable mis en évidence dans IDisposable: ce que votre mère ne vous a jamais dit sur la désallocation des ressources . Fondamentalement, à moins que votre classe soit scellée, vous devez décider si vos descendants sont susceptibles d’avoir des membres IDisposable . Et ce n’est pas quelque chose que vous pouvez prédire de façon réaliste.

Donc, si vos descendants sont susceptibles d’avoir des membres IDisposable , cela pourrait être une bonne raison pour implémenter IDisposable dans la classe de base.

La réponse courte serait non. Cependant, vous pouvez utiliser intelligemment la nature de l’exécutable Dispose() à la fin du cycle de vie de l’object. On a déjà donné un bel exemple de MVC ( Html.BeginForm )

Je voudrais souligner un aspect important de IDisposable et using() {} statement. A la fin de l’énoncé Using , la méthode Dispose() est appelée automatiquement sur l’object de contexte using (il doit bien sûr avoir implémenté une interface IDisposable ).

Il y a une raison de plus que personne n’a mentionnée (même si cela en vaut la peine): La convension dit que si quelqu’un utilise une classe qui implémente IDisposable , elle doit appeler sa méthode Dispose (explicitement ou via la déclaration ‘using’). Mais que se passe-t-il si V1 d’une classe (dans votre API publique) n’a pas besoin d’IDisposable, mais V2 le fait? Techniquement, cela ne brise pas la compatibilité en amont pour append une interface à une classe, mais à cause de cette convection , c’est le cas! Parce que les anciens clients de votre code n’appellent pas sa méthode Dispose et peuvent ne pas libérer les ressources. Le seul moyen (ou presque) de l’éviter est d’implémenter IDisposable dans tous les cas, vous pensez que vous en aurez besoin à l’avenir, pour vous assurer que vos clients appellent toujours votre méthode Dispose, qu’un jour peut être vraiment nécessaire. L’autre moyen (probablement le meilleur) consiste à implémenter le modèle lambda mentionné par JaredPar ci-dessus dans la V2 de l’API.