ReactiveUI:IObservable.Transform() 不转发通知

ReactiveUI: IObservable.Transform() does not forward Notifications

在下面的代码片段中,似乎 transform(x => x.Bar.Baz) 仅在添加或删除 sl2 中的项目时转发更改,但如果更改 Baz 的值则不会。这是预期的行为吗?怎么改,所有的修改都在transformed?

SourceList<FooClass> sl2 = new SourceList<FooClass>();    
sl2.Connect()
        .Transform(x => x.Bar.Baz)
        .Bind(out ReadOnlyObservableCollection<String> transformed)
        .Subscribe( x=> { Console.WriteLine("CHANGE from Subscribe"); } );

完整代码如下。 运行 示例也可在 GitHub

上找到
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;

namespace ReactivUI_Test
{
    class BarClass : ReactiveObject
    {
        [Reactive] public String Baz { get; set; } = "";

        public BarClass(String b) { Baz = b; }
        public BarClass() { Baz = "!!!"; }

        public override String ToString() { return Baz.ToString(); }
    }



    class FooClass : ReactiveObject
    {
        [Reactive] public BarClass Bar { get; set; } = new BarClass();

        public override String ToString() { return Bar.ToString(); }
    }


    class ViewModel: ReactiveObject
    {
        [Reactive] FooClass Foo { get; set; } = new FooClass();

        void PrintList<T> (IEnumerable<T> items)
        {
            foreach (T item in items)
            {
                Console.WriteLine(item.ToString());
            }
        }

        public ViewModel()
        {


            Console.WriteLine("===  ===");

            SourceList<FooClass> sl2 = new SourceList<FooClass>();

            FooClass fo1 = new FooClass() { Bar = new BarClass("Hello ") };
            FooClass fo2 = new FooClass() { Bar = new BarClass("World ") };
            FooClass fo3 = new FooClass() { Bar = new BarClass("Out ") };
            FooClass fo4 = new FooClass() { Bar = new BarClass("There ") };
            FooClass fo5 = new FooClass() { Bar = new BarClass("!!!!!!") };

            sl2.Add(fo1);
            sl2.Add(fo2);
            sl2.Add(fo3);
            sl2.Add(fo4);


            sl2.Connect()
                .Transform(x => x.Bar.Baz)
                .Bind(out ReadOnlyObservableCollection<String> transformed)
                .Subscribe( x=> { Console.WriteLine("CHANGE from Subscribe"); } );

            Console.WriteLine("=== Start ===");

            ((INotifyCollectionChanged)transformed).CollectionChanged += 
                new NotifyCollectionChangedEventHandler(( s,e) => Console.WriteLine("CHANGE from Event Handler"));


            Console.WriteLine("sl2: ");
            PrintList<FooClass>(sl2.Items);

            Console.WriteLine("transformed: ");
            PrintList<String>(transformed);


            Console.WriteLine("=== Send to Space ===");
            fo2.Bar.Baz = "Space";

            Console.WriteLine("sl2: ");
            PrintList<FooClass>(sl2.Items);

            Console.WriteLine("transformed: ");
            PrintList<String>(transformed);      

            Console.WriteLine("=== Add !!!! ===" );

            sl2.Add(fo5);

            Console.WriteLine("sl2: ");
            PrintList<FooClass>(sl2.Items);

            Console.WriteLine("transformed: ");
            PrintList<String>(transformed);

            Console.WriteLine("===  ===");

            Console.WriteLine("Finish");
            Console.ReadLine();

        }
    }


    class Program 
    {
        static void Main(string[] args)
        {
            ViewModel vm = new ViewModel();
        }
    }
}

输出不是我所希望的。似乎 .Transform(x => x.Bar.Baz) 只有转发更新项目被添加或从列表 'sl2' 中删除。如果 Bar.Baz 的值发生变化,这些变化不会转发到 transformed

我的输出是:

===  ===
CHANGE from Subscribe
=== Start ===
sl2:
Hello
World
Out
There
transformed:
Hello
World
Out
There
=== Send to Space ===
sl2:
Hello
Space
Out
There
transformed:
Hello
World      <--- I would have hoped to have 'Space' here
Out
There
=== Add !!!! ===
CHANGE from Event Handler   <-- I would have hoped that event handlers are triggered at World->Space
CHANGE from Subscribe       <-- I would have hoped that event handlers are triggered at World->Space
sl2:
Hello
Space
Out
There
!!!!!!
transformed:
Hello
World    <--- I would have hoped to have 'Space' here
Out
There
!!!!!!
===  ===
Finish

注意: (1) 在 transformed 中,World->Space 的更改永远不会完成。在 sl2 中,此更改已完成。

(2)CollectionChangedEvent只有在添加了in Item后才会触发。

我的问题:

如何确保 transform() 设置正确的通知?

更新

根据下面 Funk 的回答,这应该通过以下方式解决:

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz)
   ...

但是这不会改变输出。

Is this expected behaviour?

是的。与 ObservableCollection<T> 一致,仅监视对集合(或流)的更改。

当然,您想要的很常见,幸运的是,很容易实现。来自 ReactiveUI documentation:

DynamicData supports change tracking for classes that implement the INotifyPropertyChanged interface — ReactiveObjects. For example, if you'd like to do a WhenAnyValue on each element in a collection of changing objects, use the AutoRefresh() DynamicData operator

只需将 AutoRefresh 运算符添加到管道并使用接受标志的 Transform 运算符重载将 transformOnRefresh 设置为 true。

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz, true)
   ...

注意,为了帮助调试,您可以分解 IChangeSet<string> 及其 Change<string>

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz, true)
   .Do(x =>
   {
       foreach (var c in x) Console.WriteLine($"sl2 ticks {c.Item.Current}");
   })
   ...