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?
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? ).
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?
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.
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.
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 IDisposable
et 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.
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.
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();
}
}
Le meilleur moyen est
userCollection= null;
Than GC s'occupera du repos.
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();
}
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'annulationContextDisposable
vous permet de disposer dans un contexte de thread donnéMultipleAssignmentDisposable
remplace un jetable par un autre sans jeter l'ancienSerialDisposable
remplace l'ancienne dispositionbe par une autre tout en jetant l'ancienne SingleAssignmentDisposable
stocke un jetable qui ne peut pas être remplacé par un autreJe 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.
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 #
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.
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
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.
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.
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 ....
}
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 ...
... 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 ...
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.