webentwicklung-frage-antwort-db.com.de

Beste Möglichkeit, eine Liste zu erstellen

Ich habe ein Listenobjekt. Wie kann ich über die Liste verfügen?

Zum Beispiel,

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

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

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

Wenn ich userCollection = null; setze, was passiert?

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

Welches ist am besten?

27
Vivekh

Die beste Idee ist, es dem Garbage Collector zu überlassen. Ihre foreach wird nichts tun, da nur der Verweis auf null und nicht auf das Element in der Liste gesetzt wird. Das Festlegen der Liste auf null könnte tatsächlich dazu führen, dass die Speicherbereinigung später erfolgt, als sie es könnte (siehe diesen Beitrag C #: Sollen Objektvariablen null zugewiesen werden? ).

21
Cornelius

Erstens können Sie eine Liste nicht "entsorgen", da sie nicht IDisposable ist, und Sie können nicht erzwungen werden, dass gesammelt wird, da C # nicht so funktioniert.NormalerweiseSie würden hier nichts tun. Wenn vielleicht müssen wirirgendetwastun?

  • Wenn es sich um eine Methodenvariable handelt und Ihre Methode in Kürze beendet wird, tun Sie nichts: Lassen Sie den GC sich irgendwann darum kümmern, nachdem die Methode existiert hat.
  • Wenn es sich um ein Feld (eine Instanzvariable) handelt und das Objekt in Kürze den Gültigkeitsbereich verlässt, tun Sie nichts: Lassen Sie den GC sich irgendwann darum kümmern, nachdem die Instanz nicht erreichbar ist.

Das einzige Mal, wenn Sie irgendwas brauchen, ist, wenn es sich um ein Feld (oder capture Variable/Iterator-Blockvariable/etc)undder Instanz (/ delegate/iterator) handelt länger leben - dann setzen Sie das Listenfeld möglicherweise auf null. Wenn jedoch ein anderer Code noch einen Verweis auf die Liste hat, ist noch alles erreichbar.

18
Marc Gravell

Ich stimme nicht zu, dass Sie nichts tun sollten, wenn Sie die Objekte in der Liste nicht mehr benötigen. Wenn die Objekte das Interface System.IDisposable implementieren, dachte der Designer des Objekts, dass das Objekt knappe Ressourcen enthält.

Wenn Sie das Objekt nicht mehr benötigen und dem Objekt einfach null zuweisen, werden diese knappen Ressourcen erst dann freigegeben, wenn der Garbage Collector das Objekt abgeschlossen hat. In der Zwischenzeit können Sie diese Ressource nicht für etwas anderes verwenden. 

Beispiel: Stellen Sie sich vor, Sie erstellen eine Bitmap aus einer Datei und entscheiden, dass Sie weder die Bitmap noch die Datei mehr benötigen. Code könnte wie folgt aussehen:

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.

Die gute Methode wäre:

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

Die gleichen Konten für Objekte in einer Liste oder einer Sammlung. Alle Objekte in der Sammlung, die IDisposable sind, sollten verworfen werden. Code sollte wie folgt aussehen:

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();
        }
    }
}

Oder wenn Sie eine IEnumerable-Erweiterungsfunktion erstellen möchten

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

Alle Listen/Wörterbücher/Nur-Lese-Listen/Collections/etc können diese Methoden verwenden, da sie alle die IEnumerable-Schnittstelle implementieren. Sie können es sogar verwenden, wenn nicht alle Elemente in der Sequenz System.IDisposable implementieren.

15

Eine andere Idee für diesen Beitrag ... Wenn Sie sicherstellen möchten, dass alle Mitglieder einer Sammlung ordnungsgemäß entsorgt werden, können Sie die folgende Erweiterungsmethode verwenden:

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

Dies durchsucht die Sammlung nach jedem Member, das IDisposable implementiert und entsorgt. Mit dem ausgeführten Code können Sie die Liste wie folgt bereinigen:

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

Dadurch wird sichergestellt, dass alle Mitglieder die Möglichkeit haben, Ressourcen freizugeben, und die resultierende Liste ist leer.

12
jheddings

Sie haben nicht genügend Kontext bereitgestellt. Der Umfang ist hier kritisch.

Ich denke, der GC sollte intelligent genug sein, um mit dem für die Benutzer zugewiesenen Speicher und der Sammlung umzugehen, ohne irgendetwas auf null setzen zu müssen.

Wenn die Auflistung nicht benötigte Benutzer aus der Auflistung entfernt und keine anderen Objekte auf sie verweisen, werden sie auf GC angewendet, ohne dass Sie dazu Hinweise geben müssen.

Der GC bereinigt ein Objekt nicht, solange ein Live-Verweis darauf vorhanden ist. Beseitigen Sie alle Referenzen und es kann seine Arbeit erledigen.

2
duffymo

Ein weiteres Beispiel für eine Erweiterungsmethode, mit der Sie eine Liste von Objekten bereitstellen können, die die IDisposable-Schnittstelle implementieren. Dieses verwendet die LINQ-Syntax.

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

Am besten ist es 

userCollection= null;

Dann wird GC für die Ruhe sorgen.

1
Pranay Rana

Und eine generische Implementierung, die bearbeitet wird (in List<T> Methodenliste angezeigt), wenn das Element implementiert ist IDisposable

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

Auf diese Weise zu verwenden

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

Bei der Verwendung von System.Reactive.Disposeables gibt es einen viel besseren Weg: 

Initialisieren Sie einfach eine neue Eigenschaft des Typs CompositeDisposable und fügen Sie die Einwegartikel dieser Sammlung hinzu. Dann entsorgen Sie einfach diese. 

Hier ein Codebeispiel, wie Sie dies in einem typischen WPF/UWP ViewModel tun, ohne Speicherverluste zu verursachen: 

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() { /* ... */ }

implementiere IDisposable so:

    #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
}

und hier ist die Erweiterungsklasse, um die .AddRange()-Methode abzurufen: 

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

Siehe auch

  • Mit BooleanDisposable können Sie abfragen, ob das Objekt bereits gelöscht wurde
  • CancellationDisposable ist wie BooleanDisposable, aber mit einem Storno-Token
  • Mit ContextDisposable können Sie sich in einem bestimmten Thread-Kontext befinden
  • MultipleAssignmentDisposable ersetzt einen Disposable durch einen anderen, ohne den alten Disposable zu entsorgen
  • SerialDisposable ersetzt den alten Abfall durch einen anderen, während der alte weggeworfen wird 
  • SingleAssignmentDisposable speichert ein Disposable, das nicht durch ein anderes Disposable ersetzt werden kann
1
MovGP0

Ich sehe viele Antworten, die Dispose eines Objekts innerhalb einer foreach-Schleife über eine Sammlung aufrufen. Da Dispose nur das Objekt markiert, das bei der nächsten Ausführung des Garbage Collection entfernt werden soll, funktioniert es in Ordnung. Theoretisch könnte das Entsorgen eines Elements jedoch die Sammlung modifizieren und das Foreach durchbrechen. Es wäre also robuster, zuerst diese Einwegobjekte zu sammeln, die ursprüngliche Liste zu löschen und die Dispose innerhalb einer for oder while-Schleife vom Ende bis zum Entfernen aufzurufen das Objekt in jeder Iteration, z Aufruf der folgenden Methode:

    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));
            }
        }
    }

Ich verwende die DeleteItemsInList eigentlich für andere Zwecke, z. Dateien löschen: DeleteItemsInList (File.Delete))

Wie bereits erwähnt, sollte es im Allgemeinen nicht erforderlich sein, eine solche Liste zu erstellen. Ein Fall, in dem ich Artikel in einer Liste besitze, arbeitet mit Stream, ich sammle ein paar Dampfzüge, transformiere die Daten daraus, und entsorgen Sie dann diesen Strom und bewahren Sie nur meine transformierten Objekte zur weiteren Verarbeitung auf.

0
EricBDev

Wie jeder erwähnt hat, überlassen Sie GC, es ist die beste Option, und erzwingen Sie keine GC. Wenn Sie die Variable auf Null setzen, wird die Variable für den GC markiert. 

wenn Sie danach mehr erfahren: Best Practice für das Erzwingen der Garbage Collection in C #

0
user831055

Ich bin auf Szenarien gestoßen, in denen der GC bei der Verarbeitung großer Datenmengen erst dann bereinigt, wenn die Sammlung außerhalb des Gültigkeitsbereichs liegt (der GC sammelt seine Erfassung, wenn dies für richtig erachtet wird und dies möglicherweise nicht der Fall ist wenn dann die Sammlung aus dem Geltungsbereich fällt).

In diesen (seltenen) Szenarien habe ich die folgende Klasse verwendet:

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

Sie können sie dann wie eine normale Liste verwenden, z.

var myList = new DisposableList<MyObject>();

Dann rufen Sie die Dispose-Methode auf, wenn Sie fertig sind:

myList.Dispose();

Oder deklarieren Sie es alternativ in einer using-Anweisung:

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

Dies führt dann dazu, dass der GC seine Sammlung sofort ausführt, sobald die DisposableList außerhalb des Gültigkeitsbereichs liegt oder entsorgt wird.

0
user2746082

Warum möchten Sie die Liste entsorgen? Der GC erledigt das für Sie, wenn es keine Hinweise mehr gibt. 

Müllsammlung: msdn.Microsoft.com/de-de/library/0xy59wtx.aspx

0
Patrick

Ich hoffe, Sie haben eine Ausnahme wegen Speicherausfall festgestellt, wenn Sie diese Frage stellen. Wenn nicht, sollten Sie einen Test erstellen, um eine Ausnahme für Speicherausfall zu verursachen.

Vorausgesetzt, Sie haben tatsächlich Speicherprobleme, müssen Sie feststellen, was im Benutzerobjekt den gesamten Speicher belegt. Legen Sie die Eigenschaften im User-Objekt auf null fest, die den meisten Speicherplatz beanspruchen.

User.BigData = null;

Dann können Sie Ihre Benutzerliste beibehalten und den Garbage Collector die Eigenschaften bereinigen, die den gesamten Speicher beanspruchen.

0

Wenn Ihr Element in der Liste ein nicht verwaltetes Objekt ist, können Sie Dispose () für jedes Objekt aufrufen, indem Sie es iterieren. 

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

Wenn das Listenobjekt ein verwaltetes Objekt ist, müssen Sie nichts tun. GC wird aufpassen. 

0
RJN

Eine andere Idee ist, Klammern zu verwenden, die den Gültigkeitsbereich Ihrer Variablen enthalten, den Sie beibehalten möchten.

zum Beispiel.

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

Viele dieser Antworten haben so etwas wie ...

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();

Es gibt keine einzige Antwort, in der erwähnt wird, warum .Clear () hilfreich ist. Die Antwort ist die Entkopplung der Elemente in der Sammlung von der Sammlung und voneinander. 

Je mehr Sie beim Abreißen eines Objekts entkoppeln, desto größer ist die Chance, dass der Garbage Collector seine Aufgabe rechtzeitig erfüllt. Häufig entstehen erhebliche .NET-Speicherverluste aus großen Graphen von Objekten, die zu 99% nicht verwendet werden und ein Element aufweisen, auf das noch verwiesen wird. 

Ich denke, es ist eine gute Praxis, um ...

  • kündigen Sie alle Ereignisse ab, für die das Objekt abonniert wurde 
  • alle vorhandenen Sammlungen löschen 
  • und alle Mitglieder auf null setzen.

... in einer Klasse, die IDisposable implementiert.

Ich schlage nicht vor, IDisposable für alles zu implementieren und dies zu tun, ich sage, wenn es so ist, dass Sie die Implementierung von dispose implementieren müssen, können Sie es genauso gut tun. Ich implementiere IDisposable nur, wenn das Objekt ...

  • veranstaltungen abonnieren oder
  • besitzt Mitglieder, die Dispose () implementieren, wie eine DB-Verbindung oder eine Bitmap oder
  • hat andere nicht verwaltete Ressourcen.

Die einzige Zeit, in der ich nur Dispose implementieren würde, um ein Objekt zu entkoppeln, wäre, wenn Sie wissen, dass Sie einen Speicherverlust haben und die Analyse eines Memory-Profilers darauf hindeutet, dass dies hilfreich sein könnte.

0
Mick