fix: ObserveCountChanged misses count if observer has side effect - #121
awesomestmason wants to merge 2 commits into
Conversation
|
closes #120 |
|
@awesomestmason Your analysis matches what I see in the code: assigning One thing I noticed while reading around it: case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Reset when countPrev != source.Count:
+ countPrev = source.Count;
observer.OnNext(source.Count);
break;
}
- countPrev = source.Count;Moving the assignment inside the Here is a test mirroring and that it passes with the change applied: [Fact]
public void ObserveViewCountChanged_WithSideEffect()
{
var events = new List<int>();
var collection = new ObservableList<int>([]);
using var view = collection.CreateView(x => x);
using var _ = view.ObserveCountChanged().Subscribe(count =>
{
events.Add(count);
// Side effect - when count is 1, clear the list
if (count == 1) collection.Clear();
});
events.Should().BeEmpty();
collection.Add(12);
view.Count.Should().Be(0);
events.Should().BeEquivalentTo([1, 0]);
}Please feel free to take these into this PR if you like, or leave them and I will send a follow-up PR for the view overload — whichever you prefer. It would just be nice to have both overloads covered together so #120 does not have to be reopened for the view case. I have only recently started looking at this codebase, so I may well be missing some history behind the current shape of the code; someone more familiar with it should confirm. |
There was a problem hiding this comment.
🟡 Changes recommended
There are a couple of correctness gaps (double-reading Count in the handler and an order-insensitive assertion in the new regression test) that can reduce the reliability of the fix and its test.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR fixes ObserveCountChanged so that its internal countPrev tracking stays consistent with the value emitted to observers, even when OnNext has side effects that mutate the collection. It also adds a regression test that exercises re-entrant mutation (clearing the list from inside the observer).
Changes:
- Update
ObserveCountChangedhandler to assigncountPrevbefore emitting the count. - Add a new test covering observer side effects that change the collection count.
File summaries
| File | Description |
|---|---|
src/ObservableCollections.R3/ObservableCollectionR3Extensions.cs |
Adjusts ObserveCountChanged handler to avoid countPrev becoming inaccurate when OnNext causes side effects. |
tests/ObservableCollections.R3.Tests/ObservableCollectionExtensionsTest.cs |
Adds a regression test for re-entrant side effects during ObserveCountChanged notifications. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@aetos382 thank you for reviewing the PR and helping maintain this project. Good catch on the synchronized view case, I agree that it would make sense to fix both in the PR, so I have updated the PR to include it. I also added your test case to the tests file, and implemented the Copilot suggestions which I agreed with. |
The issue is that OnNext can have any number of side effects. If a side effect modifies the collection.Count, then the countPrev will be inaccurate, because the collection.Count has changed. It was not the same value that was actually emitted.
To fix this, I moved the countPrev assignment to before the emission. That way it is guaranteed that countPrev matches the value that we actually emitted.