如果数组元素发生变化,Observable会发生什么?

3

我该如何用单行代码来写这段代码呢?

public class RadGridColumnObservable
{
    public RadGridColumnObservable(RadGridView grid, IMessageBus messageBus)
    {
        this.grid = grid;
        this.messageBus = messageBus;
    }

    public IDisposable Initialize()
    {
        CreateStates();

        return Observable
            .Interval(TimeSpan.FromSeconds(1))
            .Subscribe(_ => CheckUp());
    }

    //

    private readonly RadGridView grid;
    private readonly IMessageBus messageBus;
    private readonly List<bool> states = new List<bool>();

    //

    private void CreateStates()
    {
        states.Clear();
        states.AddRange(grid.Columns.Select(it => it.IsVisible));
    }

    private void CheckUp()
    {
        if (states.SequenceEqual(grid.Columns.Select(it => it.IsVisible))) return;

        CreateStates();
        messageBus.Publish(new NotifyMessage(MessageTypes.HistoryColumnsChanged));
    }
}

这个想法是:我想检查 IsVisible 属性是否发生了变化。

我不喜欢使用这行代码:

private readonly List<bool> states = new List<bool>();

“isVisible”改变了什么值?你需要一些结构来保存要比较的值。 - J.Salas
我没有找到RadGridView中任何关于隐藏/显示列的事件,因此我使用一个线程来比较旧列和新列,以查看"IsVisible"何时更改。代码运行良好,但我想用Observable来进行优化。 - Alex Soryn
你正在使用的网格是UI网格吗?如果是,你需要使用ObserveOn来避免线程问题。 - Enigmativity
3个回答

1
您可以通过以下方式获取IsVisible的值:
private IObservable<List<bool>> CreateStatesObservable()
{
    return Observable.Interval(TimeSpan.FromSeconds(1))
                        .Select(_ => grid.Columns.Select(it => it.IsVisible));
}

然后使用Scan来跟踪您先前的值:

public void Initialize()
{
    var observable = 
            CreateStatesObservable()
                        .Scan(
                            (prev: default(List<bool>), actual: default(List<bool>)),
                            (acc, c) => (acc.actual, current))
                        .Where(values => !values.actual.SequenceEqual(values.prev))
                        .Select(_ => true);
}

或者你可以使用 Defer 并像这样进行操作:

public void Initialize2()
{
    var observable = CreateStatesObservable();

    var deferred =
        Observable.Defer(
                      () =>
                      {
                          List<bool> previous = null;

                          return observable.Select(
                              values =>
                              {
                                  if (previous is null)
                                  {
                                      previous = values;
                                      return false;
                                  }

                                  if (!values.SequenceEqual(previous))
                                  {
                                      previous = values;
                                      return true;
                                  }

                                  return false;
                              });
                      })
                  .Where(value => value);
}

两种选项都应该提供一个可观察对象,仅在列IsVisible中的其中一列发生更改时才生成值。(该值只是一个true)

编辑:

您还可以像这样使用DistinctUntilChanged()自己的IEqualityComparer

class ListComparer : IEqualityComparer<List<bool>>
{
    bool IEqualityComparer<List<bool>>.Equals(List<bool>? a, List<bool>? b)
    {
        if (a is null && b is null)
        {
            return true;
        }
        
        if (a is null || b is null)
        {
            return false;
        }
        
        return a.SequenceEqual(b);
    }

    int IEqualityComparer<List<bool>>.GetHashCode(List<bool> obj)
    {
        return obj.GetHashCode();
    }
}

然后像这样使用它:

public void Initialize()
{
    var observable = 
            CreateStatesObservable()
                        .DistinctUntilChanged(new ListComparer())
                        .Select(_ => true);
}

非常好的答案。非常感谢。 - Alex Soryn
很高兴能帮忙 : )。现在我再想一想,你也可以使用 DistinctUntilChanged() 并编写自己的相等比较器来实现 IEqualityComparer<List<bool>> - Beastlike
我更新了我的答案,并提供了DistinctUntilChanged()的示例。 - Beastlike
+1 我使用了 scan 方法,进行了测试并且 Observable.Defer 也能正常工作。但我认为 scan 方法更好。虽然我是 system.reactive 的初学者,但我真的很喜欢这个想法。 - Alex Soryn

0

我现在使用类似这样的东西:

public class RadGridColumnObservable
{
    public RadGridColumnObservable(RadGridView grid, IMessageBus messageBus)
    {
        this.grid = grid;
        this.messageBus = messageBus;
    }

    public IDisposable Initialize()
    {
        return Observable
            .Interval(TimeSpan.FromSeconds(1))
            .Scan((prev: grid.Columns.Select(it => it.IsVisible).ToList(), actual: grid.Columns.Select(it => it.IsVisible).ToList()),
                (acc, c) =>
                {
                    if (!acc.prev.SequenceEqual(acc.actual))
                        acc.prev = acc.actual;

                    acc.actual = grid.Columns.Select(it => it.IsVisible).ToList();
                    return acc;
                })
            .Where(it => !it.prev.SequenceEqual(it.actual))
            .Subscribe(it => messageBus.Publish(new NotifyMessage(MessageTypes.HistoryColumnsChanged)));
    }

    //

    private readonly RadGridView grid;
    private readonly IMessageBus messageBus;
}

但我不喜欢它,因为我使用了第二次“SequenceEqual”。


0

我认为你可以将你的Initialize()调整为以下内容:

public IDisposable Initialize()
{
    return Observable
        .Interval(TimeSpan.FromSeconds(1))
        .Select(v => grid.Columns.Select(it => it.IsVisible).ToList())
        .Scan((prev: new List<bool>(), actual: new List<bool>()),
            (acc, c) => (acc.actual, c))
        .Where(it => !it.actual.SequenceEqual(it.prev))
        .Subscribe(_ => messageBus.Publish(new NotifyMessage(MessageTypes.HistoryColumnsChanged)));
}

这应该会给你相同的结果,而且你只需要一次使用SequenceEqual()。扫描只是为了让你有“当前”和“上一个”值。在你的情况下,你不需要在其中进行检查。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接