I have an ObservableCollection of Logs that I have bound to my GUI through a property
public ObservableCollection<ILog> Logs {get; private set;}
There is a requirement to show a subset of the logs somewhere else so I have:
public ObservableCollection<ILog> LogsForDisplay
{
get
{
ObservableCollection<ILog> displayLogs = new ObservableCollection<ILog>();
foreach (Log log in Logs.ToList()) // notice the ToList()
{
if (log.Date != DateTime.Now.Day)
continue;
displayLogs.Add(log);
}
return displayLogs;
}
Before I added the "ToList()" I got exceptions occasionally about "Collection was modified; enumeration operation may not execute" Makes sense - somebody could add to Logs while I'm iterating over it. I got the idea of "ToList" from Collection was modified; enumeration operation may not execute which seems to suggest that ToList is the way to go and implies it's thread safe. But is ToList() thread safe? I assume that internally it must use the list and iterate over it? And what if someone adds to that list at the same time? Just because I haven't seen a problem doesn't mean there isn't one.
My question. Is ToList() thread safe and if not, what is the best pattern for protecting Logs? If ToList() is thread safe, do you have a reference?
Bonus question. If the requirements were to change and all I needed to display on the GUI was LogsForDisplay and NOT Logs, could I change Logs to something else that would solve the problem? Such as ImmutableList ? Then I wouldn't have to call ToList<> which I assume takes some time to make a copy.
Let me know if I can provide clarification. Thanks,
Dave
The implementation of ToList
extension method boils down to copying items from one array to another via Array.Copy
method, which, while hiding Collection was modified
error from you is not thread-safe and you may face weird behavior when underlying items are changed during Array.Copy
calll.
What I'd suggest it to use CollectionView
for binding, I've been using it for quite a long time in similar cases and faced no issues so far.
// somewhere in .ctor or other init-code
var logsForDisplay = new CollectionView(this.Logs);
logsForDisplay.Predicate = log => ((Log)log).Date == DateTime.Now.Day;
public CollectionView LogsForDisplay { get { return this.logsForDisplay; } }
You can have another CollectionView
for different use case, e.g.:
// somewhere in .ctor or other init-code
var yesterdaysLogs = new CollectionView(this.Logs);
yesterdaysLogs.Predicate = log => ((Log)log).Date == DateTime.Now.AddDays(-1).Day;
public CollectionView YesterdaysLogs{ get { return this.yesterdaysLogs; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With