本文介绍了把一个方法调用变成一个可观察的事件,好主意吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习如何将 IObservable 合并到我的代码中.下面是用于从 IObservable 打印出最新单词的简单类的两种不同方法.哪种方法更清洁?我不喜欢 WordPrinterWithCache,因为它引入了额外的状态(_lastWord)变量,并且有趣的代码现在分散在整个类中.我更喜欢 WordPrinterWithExtraSubject 因为有趣的代码本地化到构造函数.但大多数情况下,它似乎更具有功能性和反应性";我正在对两个事件"的组合做出反应:(1) 发出一个新词,以及 (2) 调用 PrintMostRecent 方法.

I'm learning how to incorporate IObservable into my code. Below are two different approaches for a simple class that prints out the most recent word from an IObservable<string>. Which is the cleaner approach? I don't like WordPrinterWithCache because it introduces additional state (the _lastWord) variable and the interesting code is now scattered throughout the class. I prefer WordPrinterWithExtraSubject because the interesting code is localized to the constructor. But mostly it seems more consistently functional and "reactive"; I am reacting to the combination of two "events": (1) a new word being emitted, and (2) the invocation of the PrintMostRecent method.

然而,我已经读到,在不是绝对必要的情况下使用 Subjects 是不可取的,并且我引入了一个不必要的 Subject.基本上我在这里做的是从方法调用生成一个可观察的,这样我就可以在更多的地方使用可观察的组合器函数.我喜欢使用可观察组合器和订阅来构建我的代码的想法,而不是使用旧式"方法调用树.我不知道这是不是个好主意.

However I have read that using Subjects is not desirable when it is not strictly necessary and I am introducing an unnecessary Subject<Unit>. Essentially what I'm doing here is generating an observable from a method call so I can use the observable combinator functions in more places. I like the idea of building up my code using the observable combinators and subscriptions rather than using "old-style" method invocation trees. I don't know if this is a good idea.

public class WordPrinterWithCache
{
    string _lastWord = string.Empty;

    public WordPrinterWithCache(IObservable<string> words)
    {
        words.Subscribe(w => _lastWord = w);
    }

    public void PrintMostRecent() => Console.WriteLine(_lastWord);
}

public class WordPrinterWithExtraSubject
{
    Subject<Unit> _printRequest = new Subject<Unit>();

    public WordPrinterWithExtraSubject(IObservable<string> words)
    {
        _printRequest
            .WithLatestFrom(words.StartWith(string.Empty), (_, w) => w)
            .Subscribe(w => Console.WriteLine(w));
    }

    public void PrintMostRecent() => _printRequest.OnNext(Unit.Default);
}

我的代码中的实际情况是我有一个 ICommand.当用户调用命令(可能通过单击按钮)时,我想对特定 observable 的最新值采取行动.例如,也许 ICommand 代表 Delete,我想删除由 IObservable 表示的列表中的选定项目.为每个 observable 保留一堆最后发出的值"缓存变量会变得很丑陋.

The actual scenario in my code is that I have an ICommand. When the user invokes the command (maybe by clicking a button), I want to take action on the most recent value of a specific observable. For example, maybe the ICommand represents Delete and I want to delete the selected item in a list that is represented by an IObservable<Guid>. It gets ugly to keep a bunch of "last emitted value" cache variables for each observable.

我倾向于采用一种类似于您在下面看到的 ICommand 实现的方法.这允许我编写像 deleteCommand.WithLatestFrom(selectedItems,(d,s)=>s).Subscribe(selected=>delete(selected));

The approach I'm leaning toward is an ICommand implementation kind of like what you see below. This allows me to write code like deleteCommand.WithLatestFrom(selectedItems,(d,s)=>s).Subscribe(selected=>delete(selected));

public class ObservableCommand : ICommand, IObservable<object>
{
    bool _mostRecentCanExecute = true;
    Subject<object> _executeRequested = new Subject<object>();

    public ObservableCommand(IObservable<bool> canExecute)
    {
        canExecute.Subscribe(c => _mostRecentCanExecute = c);
    }

    public event EventHandler CanExecuteChanged; // not implemented yet

    public bool CanExecute(object parameter) => _mostRecentCanExecute;

    public void Execute(object parameter) => _executeRequested.OnNext(parameter);

    public IDisposable Subscribe(IObserver<object> observer) => _executeRequested.Subscribe(observer);
}

推荐答案

虽然我不确定我是否会使用术语将方法调用转换为可观察事件",但我一直喜欢使用完全声明式,函数式和 Rx 驱动的代码已经有一段时间了.

While I'm not sure I'd use the term "turning a method call into an observable event", I have been a fan of using fully declarative, functional and Rx driven code for a while now.

您对 ICommand 实现的描述与我几年前写的非常接近,并且已经使用了很多次并且非常成功.此外,这已经成为我称之为反应性行为"的模式的基础,它提供了许多好处.来自我的博客:

Your description of an ICommand implementation matches very closely with one I wrote a couple of years ago and have used many times and very successfully since. Furthermore, this has become the basis for a pattern I refer to as "Reactive Behaviors" which provides numerous benefits. From my blog:

  • 促进行为驱动的开发和单元测试.
  • 促进函数式和线程安全编程实践.
  • 降低(如果做得好,可以消除)副作用的风险,因为特定行为被隔离在一个命名良好的方法中.
  • 停止代码腐烂",因为所有行为都封装在特定命名的方法中.想要新行为?添加新方法.不再想要特定的行为?刚刚删除了它.想要改变特定的行为吗?更改一种方法并知道您没有破坏任何其他方法.
  • 为聚合多个输入提供简洁的机制,并将异步流程提升到一流状态.
  • 减少对实用程序类的需求,因为数据可以作为强类型匿名类通过管道传递.
  • 防止内存泄漏,因为所有行为都返回一个一次性对象,当它被释放时会删除所有订阅并释放所有托管资源.

您可以在我的博客上阅读全文.

这篇关于把一个方法调用变成一个可观察的事件,好主意吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-31 02:03