From ba8d3c846e487fcdc5005f6819bb35b660c3bb9e Mon Sep 17 00:00:00 2001 From: Roland Pheasant Date: Tue, 19 Jul 2022 10:26:52 +0100 Subject: [PATCH] Fix shared state issue in List.Transform. Fixes #618 (#619) --- .../List/TransformFixture.cs | 18 ++++++++++++++ .../List/Internal/TransformAsync.cs | 4 ---- .../List/Internal/TransformMany.cs | 3 --- src/DynamicData/List/Internal/Transformer.cs | 24 ++++++------------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/DynamicData.Tests/List/TransformFixture.cs b/src/DynamicData.Tests/List/TransformFixture.cs index e30cc6827..e06fe7601 100644 --- a/src/DynamicData.Tests/List/TransformFixture.cs +++ b/src/DynamicData.Tests/List/TransformFixture.cs @@ -144,4 +144,22 @@ public void Update() _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); _results.Messages[0].Replaced.Should().Be(0, "Should be 1 update"); } + + + [Fact] + public void MultipleSubscribersShouldNotShareState() + { + _source.AddRange(new Person[] + { + new ("Adult1", 50), + new ("Adult2", 51) + }); + + var transformed = _source.Connect() + .Transform(o => o); + + // will throw if state of ChangeAwareList is shared + transformed.Transform(o => o).Subscribe(); + transformed.Transform(o => o).Subscribe(); + } } diff --git a/src/DynamicData/List/Internal/TransformAsync.cs b/src/DynamicData/List/Internal/TransformAsync.cs index 776b90a85..685409221 100644 --- a/src/DynamicData/List/Internal/TransformAsync.cs +++ b/src/DynamicData/List/Internal/TransformAsync.cs @@ -2,12 +2,8 @@ // Roland Pheasant licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Linq; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; -using System.Threading.Tasks; namespace DynamicData.List.Internal; diff --git a/src/DynamicData/List/Internal/TransformMany.cs b/src/DynamicData/List/Internal/TransformMany.cs index 9a76cdd43..2ff0a5ef0 100644 --- a/src/DynamicData/List/Internal/TransformMany.cs +++ b/src/DynamicData/List/Internal/TransformMany.cs @@ -2,11 +2,8 @@ // Roland Pheasant licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System; using System.Collections; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; diff --git a/src/DynamicData/List/Internal/Transformer.cs b/src/DynamicData/List/Internal/Transformer.cs index 30284724c..0204060e0 100644 --- a/src/DynamicData/List/Internal/Transformer.cs +++ b/src/DynamicData/List/Internal/Transformer.cs @@ -31,9 +31,9 @@ public Transformer(IObservable> source, Func new TransformedItemContainer(item, factory(item, prev, index)); } - public IObservable> Run() - { - return _source.Scan(new ChangeAwareList(), (state, changes) => + public IObservable> Run() => Observable.Defer(RunImpl); + + private IObservable> RunImpl() => _source.Scan(new ChangeAwareList(), (state, changes) => { Transform(state, changes); return state; @@ -43,7 +43,6 @@ public IObservable> Run() var changed = transformed.CaptureChanges(); return changed.Transform(container => container.Destination); }); - } private void Transform(ChangeAwareList transformed, IChangeSet changes) { @@ -125,7 +124,7 @@ private void Transform(ChangeAwareList transformed, IC } else { - TransformedItemContainer? toRemove = transformed.FirstOrDefault(t => ReferenceEquals(t.Source, change.Current)); + var toRemove = transformed.FirstOrDefault(t => ReferenceEquals(t.Source, change.Current)); if (toRemove is not null) { @@ -188,15 +187,9 @@ public TransformedItemContainer(TSource source, TDestination destination) public TSource Source { get; } - public static bool operator ==(TransformedItemContainer left, TransformedItemContainer right) - { - return Equals(left, right); - } + public static bool operator ==(TransformedItemContainer left, TransformedItemContainer right) => Equals(left, right); - public static bool operator !=(TransformedItemContainer left, TransformedItemContainer right) - { - return !Equals(left, right); - } + public static bool operator !=(TransformedItemContainer left, TransformedItemContainer right) => !Equals(left, right); public bool Equals(TransformedItemContainer? other) { @@ -233,9 +226,6 @@ public override bool Equals(object? obj) return Equals((TransformedItemContainer)obj); } - public override int GetHashCode() - { - return Source is null ? 0 : EqualityComparer.Default.GetHashCode(Source); - } + public override int GetHashCode() => Source is null ? 0 : EqualityComparer.Default.GetHashCode(Source); } }