Подтвердить что ты не робот

Лучший способ распорядиться списком

У меня есть объект List. Как я могу удалить список?

Например,

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

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

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

Если я установил userCollection = null;, что произойдет?

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

Какой из них лучше?

4b9b3361

Ответ 1

Лучше всего оставить его сборщику мусора. Ваш foreach ничего не сделает, поскольку только ссылка будет установлена ​​на null, а не на элемент в списке. Установка списка на null может на самом деле вызвать сбор мусора позже, чем это могло бы быть (см. Этот пост С#: должны ли объектные переменные присваиваться null?).

Ответ 2

Во-первых, вы не можете "распоряжаться" списком, так как это не IDisposable, и вы не можете заставить его собираться, так как это не так, как работает С#. Обычно вы ничего не сделали бы здесь. Итак, когда нам нужно сделать что-нибудь?

  • Если это переменная метода, и ваш метод выйдет через минуту, не делайте ничего: пусть GC беспокоится об этом в какой-то момент после того, как этот метод существует.
  • Если это поле (переменная экземпляра), и объект выходит за пределы области действия за один момент, не делайте ничего: пусть GC беспокоится об этом в какой-то момент после того, как экземпляр недоступен.

Единственный раз, когда вам нужно что-либо, - это если это поле (или заблокированная переменная/итераторная блочная переменная /etc ) и, то экземпляр (/delegate/iterator) будет долго жить дольше - тогда, возможно, установите для поля списка значение null. Обратите внимание, однако, что если какой-либо другой код все еще имеет ссылку на список, тогда все будет доступно.

Ответ 3

Я не согласен с тем, что вы ничего не должны делать, если вам больше не нужны объекты в списке. Если объекты реализуют интерфейс System.IDisposable то разработчик объекта думал, что объект содержит ограниченные ресурсы.

Если вам больше не нужен объект и просто назначить null объекту, то эти ограниченные ресурсы не освобождаются до тех пор, пока сборщик мусора не завершит объект. В то же время вы не можете использовать этот ресурс для чего-то другого.

Пример. Предположим, вы создали растровое изображение из файла и решили, что вам больше не нужны ни растровые изображения, ни файл. Код может выглядеть следующим образом:

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.

Хорошим методом будет:

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

То же самое относится к объектам в списке или к любой коллекции. Все объекты в коллекции, которые являются IDisposable, должны быть удалены. Код должен выглядеть следующим образом:

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

Или если вы хотите создать функцию расширения IEnumerable

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

Все списки/словари/списки только для чтения/коллекции /etc могут использовать эти методы, потому что все они реализуют интерфейс IEnumerable. Вы даже можете использовать его, если не все элементы в последовательности реализуют System.IDisposable.

Ответ 4

Другая идея для этого сообщения... Если вы хотели убедиться, что все члены коллекции правильно расположены, вы можете использовать следующий метод расширения:

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

Это просматривает коллекцию для любого участника, который реализует IDisposable и удаляет его. Из вашего исполняемого кода вы можете очистить список следующим образом:

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

Это гарантирует, что все участники получат возможность освобождать ресурсы, а полученный список пуст.

Ответ 5

Вы не предоставили достаточного контекста. Здесь важна область действия.

Я думаю, что GC должен быть достаточно умным, чтобы иметь дело с памятью, выделенной для пользователей и коллекции, без необходимости устанавливать что-либо в значение null.

Если коллекция удаляет пользователей, которые не нужны из коллекции, и никакие другие объекты не ссылаются на них, они будут GC'd без необходимости давать какие-либо подсказки.

GC не будет очищать объект, пока есть живая ссылка на него. Устраните все ссылки, и он может выполнить свою работу.

Ответ 6

Еще один пример метода расширения, который вы можете использовать для удаления списка объектов, реализующих интерфейс IDisposable. Этот использует синтаксис LINQ.

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

Ответ 7

Лучший способ -

userCollection= null;

Чем GC позаботится о покое.

Ответ 8

И общая реализация, которая будет работать (появится в списке методов List<T>), если элемент реализован IDisposable

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

Для использования таким образом

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

Ответ 9

Существует гораздо лучший способ использования System.Reactive.Disposeables:

Просто инициализируйте новое свойство типа CompositeDisposable и добавьте одноразовые предметы в эту коллекцию. Тогда разместите только этот.

Вот пример кода, как это сделать в типичном WPM/UWP ViewModel без внесения каких-либо утечек памяти:

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

реализовать IDisposable следующим образом:

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

и вот класс расширения для получения метода .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);
        }
    }
}

См. также

  • BooleanDisposable позволяет вам запросить, если объект уже был удален
  • CancellationDisposable подобен BooleanDisposable, но с маркером отмены
  • ContextDisposable позволяет вам размещать в заданном контексте потока
  • MultipleAssignmentDisposable заменяет одно одноразовое на другое без утилизации старого одноразового
  • SerialDisposable заменяет старое хранилище другим при удалении старого одноразового
  • SingleAssignmentDisposable хранит одноразовое хранилище, которое не может быть заменено другим одноразовым

Ответ 10

Почему вы хотите удалить список? GC сделает это за вас, если больше не будет ссылок на него.

Сбор мусора: msdn.microsoft.com/en-us/library/0xy59wtx.aspx

Ответ 12

Еще одна идея - использовать скобки, которые включают область вашей переменной, которую вы хотите сохранить.

например.

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

Ответ 13

Я столкнулся с ситуациями, когда при обработке больших объемов данных GC не очищается до тех пор, пока коллекция не выйдет из сферы действия (технически GC делает свою коллекцию, когда она сочтет нужным, и это может быть не тогда, когда коллекция выходит за рамки).

В этих (редких) сценариях я использовал следующий класс:

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

Затем вы можете использовать его так же, как обычный список, например

var myList = new DisposableList<MyObject>();

Затем вызовите метод Dispose, когда вы закончите:

myList.Dispose();

Или, наоборот, объявите его в используемом утверждении:

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

Это приводит к тому, что GC выполняет свою сборку сразу же после того, как DisposableList выходит за пределы области видимости или удаляется.

Ответ 14

Я вижу много ответов, вызывающих Dispose объекта внутри цикла foreach над коллекцией. Так как Dispose просто отмечает объект, который будет удален при следующем запуске сборщика мусора, он будет работать нормально. Теоретически, однако, удаление предмета может изменить сбор и взломать foreach, поэтому было бы более надежно сначала собирать эти одноразовые объекты, очищать исходный список и вызывать удаление в цикле for или while, начиная с конца и удаляя объект на каждой итерации, например вызывая следующий метод:

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

Я использую DeleteItemsInList для других целей, например. для удаления файлов: DeleteItemsInList (File.Delete))

Как уже отмечалось, в общем случае нет необходимости распоряжаться таким списком. Случай, когда я удаляю элемент в списке, работает с Stream, я собираю несколько пар, преобразую данные из них, а затем удаляю этот поток и сохраняю только мои преобразованные объекты для дальнейшей обработки.

Ответ 15

Надеюсь, вы столкнулись с исключением из памяти, если задаете этот вопрос, если не хотите создать тест, чтобы вызвать исключение из памяти.

Предполагая, что у вас действительно есть проблемы с памятью, вам нужно определить, что в объекте User потребляет всю вашу память. Задайте свойства в объекте User равными null, которые потребляют большую часть памяти.

User.BigData = null;

Затем вы можете сохранить список пользователей и позволить сборщику мусора очистить свойства, которые потребляют всю вашу память.

Ответ 16

Если ваш элемент в списке является объектом без управления, вы можете вызвать Dispose() для каждого объекта, итерации его.

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

Если объектом списка является управляемый объект, вам не нужно ничего делать. GC позаботится.