web-dev-qa-db-fra.com

Meilleur moyen de disposer d'une liste

Je vais avoir l'objet de la liste. Comment puis-je disposer de la liste?

Par exemple,

List<User> usersCollection =new List<User>();

User user1 = new User();
User user2 = new User()

userCollection.Add(user1);
userCollection.Add(user2);

Si je définis userCollection = null; que se passera-t-il?

foreach(User user in userCollection)
{
    user = null;
}

Lequel est le meilleur?

27
Vivekh

La meilleure idée est de laisser le ramasse-miettes . Votre foreach ne fera rien car seule la référence sera définie sur null et non pas l'élément de la liste. Définir la liste sur null pourrait en fait provoquer un garbage collection plus tard que prévu (voir cet article C #: les variables d'objet doivent-elles être affectées à null? ).

21
Cornelius

Premièrement, vous ne pouvez pas "disposer" une liste car elle n'est pas IDisposable et vous ne pouvez pas la forcer à être collectée car ce n'est pas ainsi que fonctionne C #.Typiquementvous ne feriez que rien ici. Alors, quand pourrions-nous nous devons fairerien?

  • S'il s'agit d'une variable de méthode et que votre méthode va se terminer dans un instant, ne faites rien: laissez le GC s'en préoccuper à un moment donné après que la méthode ait existé.
  • S'il s'agit d'un champ (variable d'instance) et que l'objet va sortir de sa portée dans un instant, ne faites rien: laissez le GC s'en préoccuper à un moment donné après que l'instance soit inaccessible.

La seule fois que vous avez besoin de n'importe quoi, c'est s'il s'agit d'un champ (ou capturé variable/itérateur bloc variable/etc)etl'instance (/ delegate/iterator) va vivre longtemps - alors peut-être définir le champ de liste sur null. Notez cependant que si un autre code contient toujours une référence à la liste, tout sera toujours accessible.

18
Marc Gravell

Je ne suis pas d'accord pour dire que vous ne devriez rien faire si vous n'avez plus besoin des objets de la liste. Si les objets implémentent l'interface System.IDisposable, le concepteur de l'objet pense que celui-ci contient des ressources rares.

Si vous n'avez plus besoin de l'objet et assignez simplement null à l'objet, ces ressources rares ne sont pas libérées jusqu'à ce que le ramasse-miettes finalise l'objet. En attendant, vous ne pouvez pas utiliser cette ressource pour autre chose. 

Exemple: Considérez que vous créez une image bitmap à partir d'un fichier et décidez que vous n'avez plus besoin de l'image bitmap, ni du fichier. Le code pourrait ressembler à ceci:

using System.Drawing;
Bitmap bmp = new Bitmap(fileName);
... // do something with bmp until not needed anymore
bmp = null;
File.Delete(fileName); // EXCEPTION, filename is still accessed by bmp.

La bonne méthode serait:

bmp.Dispose();
bmp = null;
File.Delete(fileName);

Les mêmes comptes pour les objets d'une liste ou de n'importe quelle collection. Tous les objets de la collection qui sont IDisposable doivent être supprimés. Le code devrait être comme:

private void EmptySequence (IEnumerable sequence)
{   // throws away all elements in the sequence, if needed disposes them
    foreach (object o in sequence)
    {
        System.IDisposable disposableObject = o as System.IDisposable;
        o = null;
        if (disposableObject != null)
        {
            disposableObject.Dispose();
        }
    }
}

Ou si vous souhaitez créer une fonction d'extension IEnumerable

public static void DisposeSequence<T>(this IEnumerable<T> source)
{
    foreach (IDisposable disposableObject in source.OfType(System.IDisposable))
    {
        disposableObject.Dispose();
    };
}

Toutes les listes/dictionnaires/listes en lecture seule/collections/etc peuvent utiliser ces méthodes car elles implémentent toutes l'interface IEnumerable. Vous pouvez même l'utiliser si tous les éléments de la séquence n'implémentent pas System.IDisposable.

15
Harald Coppoolse

Une autre idée pour ce post ... Si vous vouliez vous assurer que tous les membres d'une collection sont correctement disposés, vous pouvez utiliser la méthode d'extension suivante:

public static void DisposeAll(this IEnumerable set) {
    foreach (Object obj in set) {
        IDisposable disp = obj as IDisposable;
        if (disp != null) { disp.Dispose(); }
    }
}

Cela recherche dans la collection tous les membres qui implémentent IDisposableet en disposent. À partir de votre code d'exécution, vous pouvez nettoyer la liste de la manière suivante:

usersCollection.DisposeAll();
usersCollection.Clear();

Cela garantira que tous les membres auront la possibilité de libérer des ressources et que la liste résultante sera vide.

12
jheddings

Vous n'avez pas fourni assez de contexte. La portée est critique ici.

Je pense que le GC devrait être suffisamment intelligent pour gérer la mémoire allouée aux utilisateurs et à la collection sans rien définir à null.

Si la collection supprime les utilisateurs qui ne sont pas nécessaires de la collection et qu'aucun autre objet ne s'y réfère, ils seront référencés sans que vous ayez à fournir d'indications.

Le GC ne nettoiera pas un objet tant qu'il y a une référence en direct à celui-ci. Éliminer toutes les références et il peut faire son travail.

2
duffymo

Encore un autre exemple de méthode d’extension que vous pouvez utiliser pour disposer d’une liste d’objets qui implémentent l’interface IDisposable. Celui-ci utilise la syntaxe LINQ.

    public static void Dispose(this IEnumerable collection)
    {
        foreach (var obj in collection.OfType<IDisposable>())
        {
            obj.Dispose();
        }
    }
2
mslissap

Le meilleur moyen est 

userCollection= null;

Than GC s'occupera du repos.

1
Pranay Rana

Et une implémentation générique qui sera travaillée (apparaîtra dans la liste de méthodes List<T>) si l'élément implémenté IDisposable

public static class LinqExtensions
{
    public static void DisposeItems<T>(this IEnumerable<T> source) where T : IDisposable
    {
        foreach(var item in source)
        {
            item.Dispose();
        }
    }
}

Pour être utilisé de cette façon

if(list != null)
{
  list.DisposeItems();                
  list.Clear();
}
1
Mohsen Afshin

Il existe une bien meilleure façon d’utiliser System.Reactive.Disposeables

Il suffit d’initialiser une nouvelle propriété de type CompositeDisposable et d’ajouter les éléments jetables à cette collection. Ensuite, jetez simplement celui-ci. 

Voici un exemple de code sur la manière de le faire dans un ViewModel WPF/UWP typique sans induire de fuite mémoire 

public sealed MyViewModel : IDisposable
{
    // ie. using Serilog
    private ILogger Log => Log.ForContext<MyViewModel>();

    // ie. using ReactiveProperty 
    public ReactiveProperty<string> MyValue1 { get; } 
        = new ReactiveProperty<string>(string.Empty);

    public ReactiveProperty<string> MyValue1 { get; } 
        = new ReactiveProperty<string>(string.Empty);

    // this is basically an ICollection<IDisposable>
    private CompositeDisposable Subscriptions { get; } 
        = new CompositeDisposable();

    public MyViewModel()
    {
        var subscriptions = SubscribeToValues(); // Query
        Subscriptions.AddRange(subscriptions); // Command
    }

    private IEnumerable<IDisposable> SubscribeToValues()
    {
        yield return MyValue1.Subscribe(
            value => DoSomething1(value), 
            ex => Log.Error(ex, ex.Message), 
            () => OnCompleted()); 

        yield return MyValue2.Subscribe(
            value => DoSomething2(value),
            ex => Log.Error(ex, ex.Message), 
            () => OnCompleted()); 
    }

    private void DoSomething1(string value){ /* ... */ }
    private void DoSomething2(string value){ /* ... */ }
    private void OnCompleted() { /* ... */ }

implémenter IDisposable comme ceci:

    #region IDisposable
    private ~MyViewModel()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    private bool _isDisposed;
    private Dispose(bool disposing)
    {
        if(_isDisposed) return; // prevent double disposing

        // dispose values first, such that they call 
        // the onCompleted() delegate
        MyValue1.Dispose();
        MyValue2.Dispose();

        // dispose all subscriptions at once 
        Subscriptions.Dispose(); 

        // do not suppress finalizer when called from finalizer
        if(disposing) 
        {
            // do not call finalizer when already disposed
            GC.SuppressFinalize(this);
        }
        _isDisposed = true;
    }
    #endregion
}

et voici la classe d'extension pour obtenir la méthode .AddRange()

public static class CollectionExtensions
{
    public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> values)
    {
        foreach(var value in values)
        {
            collection.Add(value);
        }
    }
}

Voir également

  • BooleanDisposable permet de demander si l'objet a déjà été supprimé
  • CancellationDisposable est comme BooleanDisposable mais avec un jeton d'annulation
  • ContextDisposable vous permet de disposer dans un contexte de thread donné
  • MultipleAssignmentDisposable remplace un jetable par un autre sans jeter l'ancien
  • SerialDisposable remplace l'ancienne dispositionbe par une autre tout en jetant l'ancienne 
  • SingleAssignmentDisposable stocke un jetable qui ne peut pas être remplacé par un autre
1
MovGP0

Je vois beaucoup de réponses appeler Éliminer un objet dans une boucle foreach sur une collection. Comme Dispose marque simplement l’objet à supprimer à la prochaine exécution du garbage collector, cela fonctionnera normalement. Cependant, en théorie, le fait de disposer d’un élément pourrait modifier la collection et rompre la valeur de chaque élément. Il serait donc plus robuste de collecter d’abord ces objets jetables, d’effacer la liste originale et d’en appeler la suppression dans une boucle for ou while à partir de la fin et de la l'objet à chaque itération, par exemple appelant la méthode suivante:

    public static void DisposeItemsInList<T>(this IList<T> list) where T : IDisposable
    {
        DeleteItemsInList(list, item => item.Dispose());
    }

    public static void DeleteItemsInList<T>(this ICollection<T> list, Action<T> delete)
    {
        if (list is IList && !((IList)list).IsFixedSize)
        {
            while (list.Count > 0)
            {
                T last = list.Last();
                list.Remove(last);
                delete?.Invoke(last);
            }
        }
        else
        {
            for (int i = 0; i < list.Count; i++)
            {
                delete?.Invoke(list.ElementAt(i));
            }
        }
    }

J'utilise effectivement DeleteItemsInList à d'autres fins, par exemple. supprimer des fichiers: DeleteItemsInList (File.Delete))

Comme ceux-ci l'ont indiqué, dans le cas général, il ne devrait pas être nécessaire de disposer d'une telle liste . Un cas où je dispose d'un élément dans une liste fonctionne avec Stream, je collecte quelques données et en transforme les données, puis disposez ces flux et ne conservez que mes objets transformés pour un traitement ultérieur.

0
EricBDev

Comme tout le monde a mentionné la permission du GC, c'est la meilleure option et ne force pas le GC. Définir la variable sur null marquera la variable pour le CPG. 

if your after more info: Meilleure pratique pour forcer le ramassage des ordures en C #

0
user831055

J'ai rencontré des scénarios dans lesquels, lorsque de grandes quantités de données sont traitées, le GC ne nettoie pas tant que la collection n'est pas sortie de son domaine technique quand la collecte est hors de portée).

Dans ces scénarios (rares), j'ai utilisé la classe suivante:

public class DisposableList<T> : List<T>, IDisposable
{
    public void Dispose()
    {
    }
}

Vous pouvez ensuite l'utiliser comme une liste normale, par exemple.

var myList = new DisposableList<MyObject>();

Ensuite, appelez la méthode Dispose lorsque vous avez terminé:

myList.Dispose();

Ou, alternativement, déclarez-le dans une instruction using:

using (var myList = new DisposableList<MyObject>())
{
    ...
}

Cela amène ensuite le CPG à effectuer sa collecte immédiatement lorsque la liste des éléments jetables est hors de portée ou supprimée.

0
user2746082

Pourquoi voulez-vous disposer de la liste? Le GC le fera pour vous s'il n'y a plus aucune référence à cela. 

Collection de déchets: msdn.Microsoft.com/en-us/library/0xy59wtx.aspx

0
Patrick

J'espère que vous avez rencontré une exception de mémoire insuffisante si vous posez cette question. Sinon, vous devez créer un test pour provoquer une exception de mémoire insuffisante.

En supposant que vous ayez réellement des problèmes de mémoire, vous devez identifier le contenu de l'objet Utilisateur qui utilise toute votre mémoire. Définissez les propriétés de l'objet Utilisateur sur null, qui consomment le plus de mémoire.

User.BigData = null;

Vous pouvez ensuite conserver votre liste d'utilisateurs et laisser le ramasse-miettes nettoyer les propriétés qui utilisent toute votre mémoire.

0
Timothy Gonzalez

Si votre élément dans la liste est un objet non géré, vous pouvez appeler Dispose () sur chaque objet en le itérant. 

foreach(User user in userCollection)
{
user.Dispose();
}

Si l'objet liste est un objet géré, vous n'avez rien à faire. GC prendra soin de vous. 

0
RJN

Une autre idée consiste à utiliser des crochets qui incluent la portée de votre variable que vous souhaitez conserver.

par exemple.

void Function()
{
    ... some code here ....

    {   // inside this bracket the usersCollection is alive
        // at the end of the bracet the garbage collector can take care of it

        List<User> usersCollection =new List<User>();

        User user1 = new User();
        User user2 = new User()

        userCollection.Add(user1);
        userCollection.Add(user2);

        foreach(User user in userCollection)
        {

        }
    }

    ... other code here ....
}
0
Aristos

Beaucoup de ces réponses ont quelque chose comme ...

public static void DisposeAll(this IEnumerable clx) {
    foreach (Object obj in clx) 
    {
        IDisposable disposeable = obj as IDisposable;
        if (disposeable != null) 
            disposeable.Dispose();
    }
}

usersCollection.DisposeAll();
usersCollection.Clear();

Il n'y a pas une seule réponse qui mentionne pourquoi .Clear () est utile. La réponse consiste à découpler les éléments de la collection de la collection et les uns des autres. 

Plus vous découplez à la suppression d'un objet, plus le ramasse-miettes aura des chances de faire son travail rapidement. Les fuites de mémoire .NET souvent importantes résultent de grands graphiques d'objets non utilisés à 99% et dont un élément est encore référencé. 

Je pense que sa bonne pratique est de ...

  • se désabonner de tous les événements auxquels l'objet est abonné 
  • effacer toutes les collections tenues 
  • et définissez tous les membres sur null.

... dans une classe implémentant IDisposable.

Je ne suggère pas de mettre en œuvre IDisposable sur tout et de faire cela, je dis que s'il se trouve que vous devez mettre en œuvre disposer, vous pourriez aussi bien le faire. Je n'implémente IDisposable que lorsque l'objet ...

  • s'abonner à des événements ou
  • possède des membres qui implémentent Dispose (), comme une connexion à une base de données, un bitmap ou
  • a d'autres ressources non gérées.

La seule fois où j'implémenterais Dispose uniquement pour découpler un objet, ce serait lorsque vous saurez que vous avez une fuite de mémoire et que l'analyse d'un profileur de mémoire suggère que cela pourrait être utile.

0
Mick