Question Utilisation correcte de l'interface IDisposable


Je sais à la lecture la documentation MSDN que l'utilisation "primaire" du IDisposable interface est de nettoyer les ressources non managées.

Pour moi, "non géré" signifie des choses comme des connexions de base de données, des sockets, des poignées de fenêtre, etc. Mais, j'ai vu le code où le Dispose() méthode est mise en œuvre pour libérer géré ressources, ce qui me semble redondant, puisque le garbage collector devrait s'en charger pour vous.

Par exemple:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

Ma question est, est-ce que cela rend la mémoire libre de garbage collector utilisée par MyCollection plus vite que normalement?

modifier: Jusqu'à présent, les utilisateurs ont affiché de bons exemples d'utilisation d'IDisposable pour nettoyer des ressources non managées telles que des connexions de bases de données et des bitmaps. Mais supposons que _theList dans le code ci-dessus contenait un million de chaînes, et vous vouliez libérer cette mémoire à présent, plutôt que d'attendre le garbage collector. Est-ce que le code ci-dessus accomplirait cela?


1378
2018-02-11 18:12


origine


Réponses:


Le point de Dispose est libérer des ressources non gérées. Cela doit être fait à un moment donné, sinon ils ne seront jamais nettoyés. Le garbage collector ne sait pas Comment appeler DeleteHandle() sur une variable de type IntPtr, il ne sait pas qu'il s'agisse ou pas, il faut appeler DeleteHandle().

Remarque: Qu'est-ce qu'un ressource non gérée? Si vous l'avez trouvé dans Microsoft .NET Framework: il est géré. Si vous vous êtes faufilé dans MSDN, ce n'est pas géré. Tout ce que vous avez utilisé les appels P / Invoke pour sortir du monde confortable de tout ce qui est disponible dans le .NET Framwork n'est pas géré - et vous êtes maintenant responsable du nettoyage.

L'objet que vous avez créé doit exposer certains méthode, que le monde extérieur peut appeler, afin de nettoyer les ressources non gérées. La méthode peut être nommée comme vous voulez:

public void Cleanup()

public void Shutdown()

Mais à la place, il existe un nom normalisé pour cette méthode:

public void Dispose()

Il y avait même une interface créée, IDisposable, qui a juste cette méthode:

public interface IDisposable
{
   void Dispose()
}

Donc, vous faites votre objet exposer le IDisposable interface, et ainsi vous promettez que vous avez écrit cette seule méthode pour nettoyer vos ressources non managées:

public void Dispose()
{
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}

Et tu as fini. Sauf que vous pouvez faire mieux.


Que faire si votre objet a alloué 250 Mo System.Drawing.Bitmap (c'est-à-dire la classe Bitmap gérée .NET) comme une sorte de tampon de trame? Bien sûr, il s'agit d'un objet géré .NET, et le garbage collector le libérera. Mais voulez-vous vraiment laisser 250 Mo de mémoire juste assis - en attendant que le ramasse-miettes finalement venez le libérer et le libérer? Et s'il y a un connexion de base de données ouverte? Nous ne voulons certainement pas que cette connexion soit ouverte, en attendant que le GC finalise l'objet.

Si l'utilisateur a appelé Dispose() (ce qui signifie qu'ils ne prévoient plus d'utiliser l'objet) pourquoi ne pas se débarrasser de ces bitmaps inutiles et des connexions de base de données?

Alors maintenant nous allons:

  • se débarrasser des ressources non gérées (parce que nous devons), et
  • se débarrasser des ressources gérées (parce que nous voulons être utiles)

Alors mettons à jour notre Dispose() méthode pour se débarrasser de ces objets gérés:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose();
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose();
      this.frameBufferImage = null;
   }
}

Et tout est bien, sauf que vous pouvez faire mieux!


Que faire si la personne oublié appeler Dispose() sur votre objet? Ensuite, ils fuiraient certains non géré Ressources!

Remarque: Ils ne fuiront pas géré ressources, car éventuellement le garbage collector va s'exécuter, sur un thread d'arrière-plan, et libérer la mémoire associée aux objets inutilisés. Cela inclut votre objet et tous les objets gérés que vous utilisez (par exemple, le Bitmap et le DbConnection).

Si la personne a oublié d'appeler Dispose(), nous pouvons encore sauve leur bacon! Nous avons encore un moyen de l'appeler pour eux: quand le ramasse-miettes se déplace finalement pour libérer (c'est-à-dire finaliser) notre objet.

Remarque: Le garbage collector finira par libérer tous les objets gérés.   Quand il le fait, il appelle le Finalize   méthode sur l'objet. Le GC ne sait pas, ou   soin, à propos votre  Disposer méthode.   C'était juste un nom que nous avons choisi pour   une méthode que nous appelons quand nous voulons obtenir   débarrasser des trucs non gérés.

La destruction de notre objet par le Garbage collector est le parfait le temps de libérer ces ressources mal gérées non gérées. Nous faisons cela en remplaçant le Finalize() méthode.

Remarque: En C #, vous ne remplacez pas explicitement le Finalize() méthode.   Vous écrivez une méthode qui ressemble à une Destructeur C ++, et le   compilateur prend cela pour être votre mise en œuvre de la Finalize() méthode:

~MyObject()
{
    //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
    Dispose(); //<--Warning: subtle bug! Keep reading!
}

Mais il y a un bug dans ce code. Vous voyez, le garbage collector fonctionne sur un fil de fond; vous ne connaissez pas l'ordre dans lequel deux objets sont détruits. Il est tout à fait possible que dans votre Dispose() code, le géré objet que vous essayez de vous débarrasser (parce que vous vouliez être utile) n'est plus là:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
      this.frameBufferImage = null;
   }
}

Donc, ce dont vous avez besoin est un moyen pour Finalize() dire Dispose() qu'il devrait ne touchez aucun géré ressources (parce qu'ils pourrait ne pas être là plus), tout en libérant des ressources non gérées.

Le modèle standard pour ce faire est d'avoir Finalize() et Dispose() à la fois appeler un troisième(!) méthode; où vous passez un dicton booléen si vous l'appelez de Dispose() (par opposition à Finalize()), ce qui signifie qu'il est sûr de libérer des ressources gérées.

Ce interne méthode pourrait être donné un nom arbitraire comme "CoreDispose", ou "MyInternalDispose", mais est la tradition de l'appeler Dispose(Boolean):

protected void Dispose(Boolean disposing)

Mais un nom de paramètre plus utile pourrait être:

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too, but only if I'm being called from Dispose
   //(If I'm being called from Finalize then the objects might not exist
   //anymore
   if (itIsSafeToAlsoFreeManagedObjects)  
   {    
      if (this.databaseConnection != null)
      {
         this.databaseConnection.Dispose();
         this.databaseConnection = null;
      }
      if (this.frameBufferImage != null)
      {
         this.frameBufferImage.Dispose();
         this.frameBufferImage = null;
      }
   }
}

Et vous changez votre mise en œuvre de la IDisposable.Dispose() méthode pour:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
}

et votre finalizer à:

~MyObject()
{
   Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}

Remarque: Si votre objet descend d'un objet qui implémente Dispose, alors n'oubliez pas d'appeler leur base Disposez la méthode lorsque vous redéfinissez Dispose:

public Dispose()
{
    try
    {
        Dispose(true); //true: safe to free managed resources
    }
    finally
    {
        base.Dispose();
    }
}

Et tout est bien, sauf que vous pouvez faire mieux!


Si l'utilisateur appelle Dispose() sur votre objet, tout a été nettoyé. Plus tard, lorsque le garbage collector arrive et appelle Finalize, il appelle ensuite Dispose encore.

Ce n'est pas seulement un gaspillage, mais si votre objet a des références indésirables à des objets que vous avez déjà éliminés du dernier appeler pour Dispose(), vous essayerez de les éliminer à nouveau!

Vous remarquerez dans mon code que j'ai pris soin de supprimer les références aux objets que j'ai éliminés, donc je n'essaie pas d'appeler Dispose sur une référence d'objet indésirable. Mais cela n'a pas empêché un insecte subtil de s'y glisser.

Lorsque l'utilisateur appelle Dispose(): la poignée CursorFileBitmapIconServiceHandle est détruit. Plus tard, lorsque le garbage collector sera lancé, il essaiera de détruire à nouveau le même handle.

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy 
   ...
}

La façon dont vous résolvez cela est de dire au garbage collector qu'il n'a pas besoin de se préoccuper de la finalisation de l'objet - ses ressources ont déjà été nettoyées, et plus aucun travail n'est nécessaire. Vous faites cela en appelant GC.SuppressFinalize() dans le Dispose() méthode:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
   GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}

Maintenant que l'utilisateur a appelé Dispose(), nous avons:

  • libéré des ressources non managées
  • ressources gérées libérées

Il ne sert à rien que le GC exécute le finaliseur - tout est pris en charge.

Impossible d'utiliser Finalize pour nettoyer les ressources non managées?

La documentation pour Object.Finalize dit:

La méthode Finalize est utilisée pour effectuer des opérations de nettoyage sur des ressources non managées détenues par l'objet en cours avant la destruction de l'objet.

Mais la documentation MSDN dit aussi, pour IDisposable.Dispose:

Effectue des tâches définies par l'application associées à la libération, la libération ou la réinitialisation de ressources non gérées.

Alors, c'est quoi? Lequel est l'endroit pour moi pour nettoyer les ressources non gérées? La réponse est:

C'est ton choix! Mais choisissez Dispose.

Vous pouvez certainement placer votre nettoyage non géré dans le finaliseur:

~MyObject()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //A C# destructor automatically calls the destructor of its base class.
}

Le problème avec cela est que vous n'avez aucune idée quand le garbage collector arrivera à finaliser votre objet. Vos ressources natives non gérées, inutiles et non utilisées resteront jusqu'à ce que le garbage collector finalement fonctionne. Ensuite, il appellera votre méthode de finalisation; nettoyer les ressources non gérées. La documentation de Object.Finalize souligne ceci:

L'heure exacte à laquelle le finaliseur est exécuté n'est pas définie. Pour assurer la libération déterministe des ressources pour les instances de votre classe, implémentez un Fermer méthode ou fournir un IDisposable.Dispose la mise en oeuvre.

C'est la vertu d'utiliser Dispose nettoyer les ressources non gérées; vous apprenez à connaître et à contrôler lorsque des ressources non gérées sont nettoyées. Leur destruction est "déterministe".


Pour répondre à votre question initiale: Pourquoi ne pas libérer de la mémoire maintenant, plutôt que pour quand le CG décide de le faire? J'ai un logiciel de reconnaissance faciale Besoins se débarrasser de 530 Mo d'images internes à présent, puisqu'ils ne sont plus nécessaires. Quand nous ne le faisons pas: la machine broie à un arrêt de l'échange.

Lecture bonus

Pour tous ceux qui aiment le style de cette réponse (en expliquant Pourquoi, alors le Comment devient évident), je vous suggère de lire le chapitre un de COM Essential de Don Box:

En 35 pages, il explique les problèmes d'utilisation des objets binaires, et invente COM sous vos yeux. Une fois que vous réalisez le Pourquoi de COM, les 300 pages restantes sont évidentes, et juste détailler l'implémentation de Microsoft.

Je pense que tout programmeur qui a déjà traité d'objets ou de COM devrait, à tout le moins, lire le premier chapitre. C'est la meilleure explication de n'importe quoi.

Lecture bonus supplémentaire

Quand tout ce que vous savez est faux par Eric Lippert

Il est donc très difficile d'écrire un finaliseur correct,   et le meilleur conseil que je peux vous donner est de ne pas essayer.


2286
2018-02-11 18:20



IDisposable est souvent utilisé pour exploiter le using déclaration et profiter d'un moyen facile de faire un nettoyage déterministe des objets gérés.

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}

54
2018-02-11 18:42



L'objectif du modèle Dispose est de fournir un mécanisme pour nettoyer les ressources gérées et non gérées et lorsque cela se produit dépend de la façon dont la méthode Dispose est appelée. Dans votre exemple, l'utilisation de Dispose n'effectue pas réellement de disposition relative à la disposition, car l'effacement d'une liste n'a aucun impact sur la disposition de cette collection. De même, les appels pour mettre les variables à zéro n'ont pas non plus d'impact sur le GC.

Vous pouvez jeter un oeil à cette article pour plus de détails sur la façon d'implémenter le pattern Dispose, mais cela ressemble à ceci:

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

La méthode qui est la plus importante ici est le Dispose (bool), qui fonctionne réellement sous deux circonstances différentes:

  • disposer == true: la méthode a été appelée directement ou indirectement par le code d'un utilisateur. Les ressources gérées et non gérées peuvent être éliminées.
  • disposer == false: la méthode a été appelée par le moteur d'exécution depuis l'intérieur du finaliseur et vous ne devez pas référencer d'autres objets. Seules les ressources non gérées peuvent être éliminées.

Le problème de laisser simplement le GC s'occuper du nettoyage est que vous n'avez aucun contrôle réel sur le moment où le GC va exécuter un cycle de collecte (vous pouvez appeler GC.Collect (), mais vous ne devriez pas) afin que les ressources restent autour plus longtemps que nécessaire. Rappelez-vous que l'appel de Dispose () ne provoque pas réellement un cycle de collecte ou n'implique en aucun cas que le GC collecte / libère l'objet; il fournit simplement les moyens de nettoyer de façon plus déterministe les ressources utilisées et indique au GC que ce nettoyage a déjà été effectué.

Le point entier de IDisposable et le modèle de disposition n'est pas de libérer immédiatement la mémoire. Le seul moment où un appel à Dispose aura même une chance de libérer immédiatement de la mémoire est quand il gère le scénario de disposition == false et manipule des ressources non gérées. Pour le code managé, la mémoire ne sera pas réellement récupérée avant que le GC n'exécute un cycle de collecte, sur lequel vous n'avez aucun contrôle (autre que d'appeler GC.Collect (), ce que j'ai déjà mentionné n'est pas une bonne idée).

Votre scénario n'est pas vraiment valide puisque les chaînes dans .NET n'utilisent pas de ressources non-émises et n'implémentent pas IDisposable, il n'y a aucun moyen de les forcer à être "nettoyées".


36
2018-02-11 20:21



Il ne devrait pas y avoir d'autres appels aux méthodes d'un objet après que Dispose a été appelé (bien qu'un objet devrait tolérer d'autres appels à Dispose). Par conséquent, l'exemple de la question est stupide. Si Dispose est appelé, l'objet lui-même peut être ignoré. Ainsi, l'utilisateur doit simplement ignorer toutes les références à cet objet entier (les mettre à null) et tous les objets connexes internes à lui seront automatiquement nettoyés.

En ce qui concerne la question générale sur la gestion / non gérée et la discussion dans d'autres réponses, je pense que toute réponse à cette question doit commencer par une définition d'une ressource non gérée.

Cela revient à dire qu'il y a une fonction que vous pouvez appeler pour mettre le système dans un état, et il y a une autre fonction que vous pouvez appeler pour le sortir de cet état. Maintenant, dans l'exemple typique, le premier peut être une fonction qui retourne un handle de fichier, et le second peut être un appel à CloseHandle.

Mais - et c'est la clé - ils pourraient être n'importe quelle paire de fonctions. L'un construit un état, l'autre l'arrache. Si l'état a été créé mais pas encore supprimé, une instance de la ressource existe. Vous devez organiser le démontage au bon moment - la ressource n'est pas gérée par le CLR. Le seul type de ressource géré automatiquement est la mémoire. Il y a deux sortes: le GC, et la pile. Les types de valeur sont gérés par la pile (ou en attelant un trajet à l'intérieur des types de référence), et les types de référence sont gérés par le GC.

Ces fonctions peuvent provoquer des modifications d'état qui peuvent être librement entrelacées ou doivent être parfaitement imbriquées. Les changements d'état peuvent être threadsafe, ou ils pourraient ne pas.

Regardez l'exemple dans la question de Justice. Les modifications de l'indentation du fichier journal doivent être parfaitement imbriquées ou tout va mal. En outre, ils sont peu susceptibles d'être threadsafe.

Il est possible de faire un tour avec le garbage collector pour nettoyer vos ressources non managées. Mais seulement si les fonctions de changement d'état sont threadsafe et que deux états peuvent avoir des durées de vie qui se chevauchent de quelque façon que ce soit. L'exemple d'une ressource de Justice ne doit donc PAS avoir de finaliseur! Cela n'aiderait personne.

Pour ces types de ressources, vous pouvez simplement mettre en œuvre IDisposable, sans finalizer. Le finaliseur est absolument optionnel - il doit l'être. Ceci est glissé ou même pas mentionné dans de nombreux livres.

Vous devez ensuite utiliser le using déclaration d'avoir une chance de faire en sorte que Dispose est appelé. C'est essentiellement comme faire un tour avec la pile (de même que le finaliseur est au GC, using est à la pile).

La partie manquante est que vous devez écrire manuellement Dispose et l'appeler sur vos champs et votre classe de base. Les programmeurs C ++ / CLI n'ont pas à faire cela. Le compilateur l'écrit pour eux dans la plupart des cas.

Il y a une alternative, que je préfère pour les états qui s'emboîtent parfaitement et ne sont pas threadsafe (en dehors de toute autre chose, éviter IDisposable vous évite d'avoir un argument avec quelqu'un qui ne peut résister à l'ajout d'un finaliseur à chaque classe implémentant IDisposable) .

Au lieu d'écrire une classe, vous écrivez une fonction. La fonction accepte un délégué pour rappeler:

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

Et puis un exemple simple serait:

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

Le lambda transmis sert de bloc de code, c'est comme si vous faisiez votre propre structure de contrôle pour servir le même but que using, sauf que vous n'avez plus aucun danger que l'appelant en abuse. Il n'y a aucun moyen qu'ils peuvent échouer à nettoyer la ressource.

Cette technique est moins utile si la ressource est susceptible de chevaucher des durées de vie, car vous voulez alors pouvoir créer la ressource A, puis la ressource B, puis détruire la ressource A et ensuite la ressource B. Vous ne pouvez pas faire cela si vous avez forcé l'utilisateur à nicher parfaitement comme ça. Mais alors vous devez utiliser IDisposable (mais toujours sans finalizer, sauf si vous avez implémenté threadsafety, ce qui n'est pas gratuit).


17
2018-02-11 19:31



Scénarios J'utilise IDisposable: nettoyer les ressources non gérées, se désinscrire des événements, fermer les connexions

L'idiome que j'utilise pour implémenter IDisposable (pas threadsafe):

class MyClass : IDisposable {
    // ...

    #region IDisposable Members and Helpers
    private bool disposed = false;

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                // cleanup code goes here
            }
            disposed = true;
        }
    }

    ~MyClass() {
        Dispose(false);
    }
    #endregion
}

14
2018-02-11 18:20



Si MyCollection va être ramassé de toute façon, alors vous ne devriez pas avoir à le jeter. Ce faisant, le CPU sera plus que nécessaire et pourra même invalider une analyse pré-calculée que le garbage collector a déjà effectuée.

j'utilise IDisposable pour faire les choses comme s'assurer que les threads sont correctement éliminés, ainsi que les ressources non gérées.

MODIFIER En réponse au commentaire de Scott:

La seule fois où les métriques de performance du GC sont affectées est quand un appel de [GC] Collect () est fait "

Conceptuellement, le GC maintient une vue du graphe de référence d'objet, et toutes les références à partir des trames de pile des fils. Ce tas peut être assez volumineux et s'étendre sur plusieurs pages de mémoire. En tant qu'optimisation, le GC met en cache son analyse des pages qui ont peu de chances de changer très souvent afin d'éviter une nouvelle analyse de la page inutilement. Le GC reçoit une notification du noyau lorsque les données d'une page changent, ainsi il sait que la page est sale et nécessite une nouvelle analyse. Si la collection est dans Gen0, il est probable que d'autres éléments de la page changent également, mais cela est moins probable dans Gen1 et Gen2. Pour l'anecdote, ces hooks n'étaient pas disponibles sous Mac OS X pour l'équipe qui a porté le GC vers Mac afin de faire fonctionner le plug-in Silverlight sur cette plate-forme.

Autre point contre la destruction inutile des ressources: imaginez une situation où un processus est en cours de déchargement. Imaginez aussi que le processus est en cours depuis un certain temps. Les chances sont que beaucoup de pages de mémoire de ce processus ont été échangées sur le disque. À tout le moins, ils ne sont plus dans le cache L1 ou L2. Dans une telle situation, il ne sert à rien qu'une application en cours de déchargement retourne toutes ces pages de données et de code en mémoire pour libérer les ressources qui seront libérées par le système d'exploitation quand le processus se terminera. Cela s'applique aux ressources gérées et même à certaines ressources non gérées. Seules les ressources qui maintiennent en vie des threads non-background doivent être éliminées, sinon le processus restera actif.

Maintenant, lors de l'exécution normale, il y a des ressources éphémères qui doivent être nettoyées correctement (comme le fait remarquer @fezmonkey connexions de base de données, prises, poignées de fenêtre) pour éviter les fuites de mémoire non managées. Ce sont les types de choses qui doivent être éliminées. Si vous créez une classe qui possède un thread (et par owns je veux dire qu'il l'a créé et qu'il est donc responsable de s'assurer qu'il s'arrête, au moins par mon style de codage), alors cette classe doit probablement implémenter IDisposable et déchirer le fil pendant Dispose.

Le framework .NET utilise le IDisposable interface comme un signal, même avertissement, aux développeurs que la classe doit être disposé. Je ne peux pas penser à des types dans le cadre qui mettent en œuvre IDisposable (à l'exclusion des implémentations d'interface explicites) où l'élimination est facultative.


11
2018-02-11 18:19



Oui, ce code est complètement redondant et inutile et il ne fait pas que le garbage collector fasse quelque chose qu'il ne ferait pas autrement (une fois qu'une instance de MyCollection est hors de portée, c'est-à-dire). .Clear() appels.

Répondez à votre edit: Sort of. Si je fais ceci:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

C'est fonctionnellement identique à ceci à des fins de gestion de la mémoire:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

Si vous avez vraiment vraiment besoin de libérer la mémoire à cet instant, appelez GC.Collect(). Cependant, il n'y a aucune raison de le faire ici. La mémoire sera libérée quand c'est nécessaire.


10
2018-06-03 21:07



Si tu veux supprimer maintenant, utilisation mémoire non gérée.

Voir:


7
2018-02-11 21:08