Этот объект вспоминается коллекционером Gargabe в этом случае? - PullRequest
0 голосов
/ 27 мая 2018

У меня есть основной метод, который имеет этот код:

List<MyType> myList = openDialog();

Вызов openDialog, который открывает диалоговое окно, которое возвращает список с выбранными элементами, диалоговое окно для выбора элементов.

private List<MyType> openDialog()
{
    MyView myView = new MyView();
    MyViewModel myViewModel = new MyViewModel();
    myView.DataContext = myViewModel;
    myView.ShowDialog();

    return myViewModel.Result;        
}

myViewModel.Result - это коллекция, в которой есть selectedItems таблицы данных в представлении.

Мой вопрос заключается в том, как я возвращаю свойство Result ViewModel, я не уверен, что myViewModelбудет вспоминаться сборщиком мусора или нет, потому что он все еще имеет ссылку на него.

Чтобы избежать этого, я делаю это:

private List<MyType> openDialog()
{
    MyView myView = new MyView();
    MyViewModel myViewModel = new MyViewModel();
    myView.DataContext = myViewModel;
    myView.ShowDialog();

    return new List<MyType>(myViewModel.Result);        
}

В ответ я создаюновый список, чтобы избежать ссылки на свойство Result и обеспечить повторный выбор объекта myViewModel, но я хотел бы знать, есть ли способ избежать создания нового списка.

Ответы [ 3 ]

0 голосов
/ 27 мая 2018

Этот вопрос немного запутан.Однако давайте проверим это.

Учитывая

public class MyGCCollectClass
{
   public List<Version> Garbage { get; set; }

   public List<int> MyInnocentList { get; set; }

   public List<int> Main()
   {
      Garbage = new List<Version>();
      for (var i = 0; i < 10000000; i++)
      {
         Garbage.Add(new Version());
      }

      MyInnocentList = new List<int>();
      return MyInnocentList;
   }
}

Мы можем смотреть на то, что собирается, а не

// Lets start from a collect
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Memory used before collection:       {0:N0}", GC.GetTotalMemory(false));

// create a class/viewmodel
var gcCollectClass = new MyGCCollectClass();

// create a world of garbage
// return back a list that is part of the class
var list = gcCollectClass.Main();

// lets see whats GC has
Console.WriteLine("Memory used: {0:N0}", GC.GetTotalMemory(true));

// make sure our garbage is still alive
Console.WriteLine(gcCollectClass.Garbage.Count);

// Kill the original class
gcCollectClass = null;

// Force a collection
GC.Collect();
GC.WaitForPendingFinalizers();

// double check the list is still alive
Console.WriteLine(list.Count);

// Lets make sure we havent caused a memory leak
Console.WriteLine("Memory used after full collection:   {0:N0}", 
GC.GetTotalMemory(true));

Вывод

Memory used before collection:       30,088
Memory used:   307,138,940
10000000
1
Memory used after full collection:   29,968

Дело в том, что если вы возвращаете список из своего класса и его часть класса, это не значит, что его возвращение остановит очистку вашего viewmodal.Он «будет» собран в этой ситуации, и нет утечек памяти

Дальнейшее чтение

Почему C # не поддерживает возврат ссылок?

Память в .NET - что идет куда

Слот памяти для переменной хранится либо в стеке, либо в куче.Это зависит от контекста, в котором он объявлен:

  1. Каждая локальная переменная (т. Е. Объявленная в методе) хранится в стеке.Это включает в себя переменные ссылочного типа - сама переменная находится в стеке, но помните, что значение переменной ссылочного типа является только ссылкой (или нулем), а не самим объектом.Параметры метода также считаются локальными переменными, но если они объявлены с модификатором ref, они не получают свой собственный слот, а делят его с переменной, используемой в вызывающем коде.См. Мою статью о передаче параметров для более подробной информации.
  2. Переменные экземпляра для ссылочного типа всегда находятся в куче.Вот где сам объект «живет».
  3. Переменные экземпляра для типа значения хранятся в том же контексте, что и переменная, которая объявляет тип значения.Слот памяти для экземпляра эффективно содержит слоты для каждого поля в экземпляре.Это означает (учитывая две предыдущие точки), что переменная структуры, объявленная в методе, всегда будет в стеке, тогда как переменная структуры, которая является полем экземпляра класса, будет в куче.
  4. Каждая статическаяпеременная хранится в куче, независимо от того, объявлена ​​ли она в ссылочном типе или типе значения.Всего есть только один слот, независимо от того, сколько экземпляров создано.(Однако для того, чтобы существовал один слот, не нужно создавать никаких экземпляров.) Детали того, в какой именно куче находятся переменные, сложны, но подробно объяснены в статье MSDN на эту тему.

https://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/

0 голосов
/ 29 мая 2018

Вы не опубликовали реализацию MyViewModel, но List<MyType>, возвращенный из модели представления, не помешает модели представления собирать мусор.Вы можете подтвердить это самостоятельно, используя WeakReference:

private void Test(object sender, RoutedEventArgs e)
{
    Window myView = new Window();
    MyViewModel myViewModel = new MyViewModel();
    myView.DataContext = myViewModel;
    myView.ShowDialog();

    List<MyType> result = myViewModel.Result;

    WeakReference viewModelWeakReference = new WeakReference(myViewModel);
    myView.DataContext = null;
    myViewModel = null;

    GC.Collect();
    GC.WaitForPendingFinalizers();

    bool isViewModelAlive = viewModelWeakReference.IsAlive; //=false
}
...
public class MyViewModel
{
    public List<MyType> Result { get; } = new List<MyType>() { new MyType(), new MyType() };
}

isViewModelAlive равно false и result.Count равно 2 при запуске приведенного выше примера кода, то есть модель представления была собрана, ноList<MyType> остается в памяти.

Обратите внимание, что WeakReference.IsAlive вернет true, если вы держите строгую ссылку на метод, который выполняет тест GC: * ​​1016 *

List<MyType> _result;
private void Button_Click(object sender, RoutedEventArgs e)
{
    _result = openDialog();
}

private List<MyType> openDialog()
{
    Window myView = new Window();
    MyViewModel myViewModel = new MyViewModel();
    myView.DataContext = myViewModel;
    myView.ShowDialog();

    List<MyType> result = myViewModel.Result;

    WeakReference viewModelWeakReference = new WeakReference(myViewModel);
    myView.DataContext = null;
    myViewModel = null;

    GC.Collect();
    GC.WaitForPendingFinalizers();

    bool isViewModelAlive = viewModelWeakReference.IsAlive;

    return result;
}

Но myViewModel по-прежнему будет иметь право на сборку мусора, как только метод вернется, поскольку у него нет корней GC.

Так что нет необходимости создавать еще List<MyType> здесь.

0 голосов
/ 27 мая 2018

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

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

Однако, чтобы удовлетворить свое любопытство, вы можете проверить, является ли объект GCed , используя ответ здесь .

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