我正在学习如何将IObservable
合并到我的代码中。下面是一个简单类的两种不同方法,它打印出来自IObservable<string>
的最新单词。哪种方法更清洁?我不喜欢WordPrinterWithCache
,因为它引入了额外的状态(_lastWord
)变量,而有趣的代码现在分散在整个班级中。我更喜欢WordPrinterWithExtraSubject
因为有趣的代码本地化为构造函数。但大多数情况似乎更具功能性和反应性&#34 ;;我正在对两个&#34;事件&#34;的组合作出反应:(1)发出一个新单词,以及(2)调用PrintMostRecent
方法。
但是我已经读到,如果不是绝对必要的话,使用主题是不可取的,我正在引入一个不必要的Subject<Unit>
。基本上我在这里做的是从方法调用生成一个observable,所以我可以在更多的地方使用可观察的组合函数。我喜欢使用可观察的组合器和订阅来构建我的代码而不是使用&#34; old-style&#34;方法调用树。我不知道这是不是一个好主意。
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
。当用户调用命令时(可能通过单击按钮),我想对特定可观察对象的最新值采取措施。例如,ICommand
可能代表Delete
,我想删除由IObservable<Guid>
表示的列表中的所选项目。保持一堆&#34;最后一次发射的价值变得非常丑陋。每个observable的缓存变量。
我倾向于的方法是ICommand
实现类似于您在下面看到的内容。这允许我编写像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);
}
答案 0 :(得分:2)
因此,作为一般规则(指南),我强烈建议不要将IObservable<T>
作为方法的参数。显而易见的警告是,如果该方法是新的Rx运算符,例如Select
,MySpecialBuffer
,Debounce
等。
这里的理论是IObservable<T>
是一种回调机制。它允许某些东西回调到另一个它不知道的东西。但是在这种情况下,您可以了解IObservable<T>
(参数)和另一个(WordPrinterWithCache
)。
那么为什么会有这个额外的间接层呢?将值推送到IObservable<T>
只能改为调用WordPrinterWithCache
实例的方法。
在这种情况下,只需在另一件事上调用一个方法
public class WordPrinterWithCache
{
private string _lastWord = string.Empty;
public void SetLastWord(string word)
{
_lastWord = word;
}
public void PrintMostRecent() => Console.WriteLine(_lastWord);
}
现在这个课开始看起来毫无意义,但那可能没问题。简单就是好。
使用Rx帮助您进行分层。
上游图层依赖于下游图层。他们将直接在下游层调用方法(发出命令)。
下游层无法访问上游层。因此,要向它们公开数据,它们可以从方法返回值,也可以公开回调。 GoF Observer模式,.NET Events和Rx是向上游层提供回调的方法。
答案 1 :(得分:0)
虽然我不确定我是否会使用“将方法调用转换为可观察事件”这一术语,但我一直喜欢使用完全声明,函数和Rx驱动的代码。
您对ICommand实现的描述与我几年前写过的描述非常接近,并且已经使用了很多次并非常成功。此外,这已经成为我称之为“反应行为”的模式的基础,其提供了许多益处。来自我的博客:
- 促进行为驱动的开发和单元测试。
- 促进功能和线程安全的编程实践。
- 降低(以及如果做得好,可以消除)副作用的风险,因为特定的行为是在一个名为方法的方法中被隔离的。
- 停止'代码腐烂',因为所有行为都封装在特定命名的方法中。想要新的行为吗?添加新方法。不再需要特定的行为了吗?刚刚删除它。想要改变一个特定的行为?改变一种方法,并知道你没有破坏任何其他方法。
- 提供用于聚合多个输入的简明机制,并将异步流程提升到一流状态。
- 减少对实用程序类的需求,因为数据可以作为强类型的匿名类传递给管道。
- 防止内存泄漏,因为所有行为都返回一次性处理,当处置时删除所有订阅并处置所有托管资源。
您可以阅读完整的文章on my blog。