In the following method, when I copy the IEnumerable
result to a list with ToList()
, no finalizers are called at the ObservableCollection
's IList
constructor, and the IDisposable
view-models only get finalized upon program closure:
private void ReadHeaders()
{
IList<PlanningGridHeaderVM> viewModelSet =
(from header in _planningGridService.ReloadHeaders().OrderBy(x => x.PlanNumber)
select new PlanningGridHeaderVM((PlanningGridHeader)header)).ToList();
foreach (PlanningGridHeaderVM headerVM in viewModelSet)
{
ItemEditedEventManager.AddHandler(headerVM, OnItemEdited);
PropertyChangedEventManager.AddHandler(headerVM, OnPropertyChanged, string.Empty);
}
this.ViewModels = new ObservableCollection<PlanningGridHeaderVM>(viewModelSet);
CollectionChangedEventManager.AddHandler(this.ViewModels, this.OnCollectionChanged);
}
However, when I use the IEnumerable
result directly in the ObservableCollection
's IEnumerable
constructor, my Debug output informs me that all of the IDisposable
view-model items are being finalized by the GC at that point (this is what puzzles me).
Nevertheless, I still get the desired collection on-screen, and upon program closure, they all get finalized again (this time, as expected):
private void ReadHeaders()
{
IEnumerable<PlanningGridHeaderVM> viewModelSet =
(from header in _planningGridService.ReloadHeaders().OrderBy(x => x.PlanNumber)
select new PlanningGridHeaderVM((PlanningGridHeader)header));
foreach (PlanningGridHeaderVM headerVM in viewModelSet)
{
ItemEditedEventManager.AddHandler(headerVM, OnItemEdited);
PropertyChangedEventManager.AddHandler(headerVM, OnPropertyChanged, string.Empty);
}
this.ViewModels = new ObservableCollection<PlanningGridHeaderVM>(viewModelSet);
CollectionChangedEventManager.AddHandler(this.ViewModels, this.OnCollectionChanged);
}
Why? The MSDN pages for both (here and here) say "The elements are copied onto the ObservableCollection in the same order they are read by the enumerator of the collection."
In the second case, you're actually evaluating the query twice. Once when you use foreach
, and then another time when you create the ObservableCollection
. You'll end up with two distinct sets of objects, which you could validate by adding some debug information in the PlanningGridHeaderVM
constructor.
Iterating over the query results the first time doesn't actively finalize it - there's no such concept. However, the objects returned will be immediately eligible for garbage collection unless your AddHandler
methods retain a reference to them.
In the first case, you're only evaluating the query once, because ToList()
will materialize the query. It's basically taking the query, evaluating it and remembering the results - so then when you iterate over it with foreach
and then create an ObservableCollection
from it, both of those loops will operate on the same collection of objects.
See more on this question at Stackoverflow