CodeGo.net>如何关联功能输入和输出?
作者:互联网
考虑下面的简单程序.它具有可观察的整数,并具有计算最近发布的整数是偶数还是奇数的功能.出乎意料的是,该程序会在报告该数字已更改之前报告最近的数字是否为偶数/奇数.
static void Main(string[] args) {
int version = 0;
var numbers = new Subject<int>();
IObservable<bool> isNumberEven = numbers.Select(i => i % 2 == 0);
isNumberEven
.Select(i => new { IsEven = i, Version = Interlocked.Increment(ref version) })
.Subscribe(i => Console.WriteLine($"Time {i.Version} : {i.IsEven}"));
numbers
.Select(i => new { Number = i, Version = Interlocked.Increment(ref version) })
.Subscribe(i => Console.WriteLine($"Time {i.Version} : {i.Number}"));
numbers.OnNext(1);
numbers.OnNext(2);
numbers.OnNext(3);
Console.ReadLine();
}
输出为:
Time 1 : False
Time 2 : 1
Time 3 : True
Time 4 : 2
Time 5 : False
Time 6 : 3
我认为更改数量会引发一系列下游影响,这些影响将在订购发生时报告.交换订购顺序将交换结果报告的方式.我知道rx是异步的,事情可能会以不确定的顺序发生.如果我在函数中使用.Delay()或网络调用,则不能确定何时报告结果.但是在这种情况下,我感到非常惊讶.
为什么这很重要?我认为这意味着,如果我想尝试关联函数的输入和输出(例如打印数字以及它们是偶数还是奇数),则必须在输出结果中包括输入参数,如下所示:
var isNumberEven = numbers.Select(i => new {
Number = i,
IsEven = i % 2 == 0
});
我以为我可以构建一堆小的简单函数,然后使用rx运算符组合它们以完成复杂的计算.但是也许我不能使用rx运算符来组合/合并/关联结果.定义每个函数时,我必须自己关联输入和输出.
在某些情况下,我可以使用rx运算符来关联结果.如果每个输入都生成一个输出,则可以压缩两个.但是,一旦您执行了节气门之类的操作,输入就不再起作用.
该程序的该版本似乎确实以合理的方式报告了数字是偶数还是奇数.
static void Main(string[] args) {
var numbers = new Subject<int>();
var isNumberEven = numbers.Select(i => i % 2 == 0);
var publishedNumbers = numbers.Publish().RefCount();
var report =
publishedNumbers
.GroupJoin(
isNumberEven,
(_) => publishedNumbers,
(_) => Observable.Empty<bool>(),
(n, e) => new { Number = n, IsEven = e })
.SelectMany(i => i.IsEven.Select(j => new { Number = i.Number, IsEven = j }));
report.Subscribe(i => Console.WriteLine($"{i.Number} {(i.IsEven ? "even" : "odd")}"));
numbers.OnNext(1);
numbers.OnNext(2);
numbers.OnNext(3);
Console.ReadLine();
}
输出如下:
1 odd
2 even
3 odd
但是我不知道这是一个幸运的巧合还是我可以依靠它. Rx中的哪些操作以确定的顺序发生?哪些是不可预测的?我是否应该定义所有函数以在结果中包括输入参数?
解决方法:
确定性地,您的第一个程序的行为与我期望的完全相同.
I understand that rx is asynchronous and it is possible for things to happen in non-deterministic order.
如果引入非确定性行为(例如并发/调度),则事情只会以不确定性顺序发生,否则Rx是确定性的.
这里有几个问题/误解.
1)外部状态可变-版本
2)主题的使用(但在此示例中根本不是一个问题)
3)对回调的发出方式有误解.
让我们只关注3).如果我们将代码带入其基本的调用站点,则可能会发现Rx的底层很简单.
number.OnNext(1);主题将按订阅顺序查找其订阅,然后按每个订阅的顺序查找OnNext.
IObservable<bool> isNumberEven = numbers.Select(i => i % 2 == 0);
isNumberEven
.Select(i => new { IsEven = i, Version = Interlocked.Increment(ref version) })
.Subscribe(i => Console.WriteLine($"Time {i.Version} : {i.IsEven}"));
也可以减少到
numbers.Select(i => i % 2 == 0)
.Select(i => new { IsEven = i, Version = Interlocked.Increment(ref version) })
.Subscribe(i => Console.WriteLine($"Time {i.Version} : {i.IsEven}"));
有人可能会争辩说,由于isNumberEven从未在其他任何地方使用,因此您应该将其简化为该数字.
这样我们可以看到我们有第一个订户.
有效运行的代码是这样
private void HandleOnNext(int i)
{
var isEven = i % 2 == 0
var temp = new { IsEven = isEven , Version = Interlocked.Increment(ref version) };
Console.WriteLine($"Time {temp .Version} : {temp .IsEven}");
}
我们的第二个订阅者(因为.Subscribe(方法是在偶数订阅之后调用的),是数字订阅者.
他的代码可以有效地归结为
private void HandleOnNext(int i)
{
var temp = new { Number = i, Version = Interlocked.Increment(ref version) };
Console.WriteLine($"Time {temp.Version} : {temp.Number}");
}
因此,一旦您完全解构了代码,便基本上得到了以下结果
void Main()
{
int version = 0;
//numbers.OnNext(1);
ProcessEven(1, ref version);
ProcessNumber(1, ref version);
//numbers.OnNext(2);
ProcessEven(2, ref version);
ProcessNumber(2, ref version);
//numbers.OnNext(3);
ProcessEven(3, ref version);
ProcessNumber(3, ref version);
}
// Define other methods and classes here
private void ProcessEven(int i, ref int version)
{
var isEven = i % 2 == 0;
var temp = new { IsEven = isEven, Version = Interlocked.Increment(ref version) };
Console.WriteLine($"Time {temp.Version} : {temp.IsEven}");
}
private void ProcessNumber(int i, ref int version)
{
var temp = new { Number = i, Version = Interlocked.Increment(ref version) };
Console.WriteLine($"Time {temp.Version} : {temp.Number}");
}
一旦所有回调和订阅都得到确认,您就可以看到这绝非偶然,一切都是确定性的.
Should I be defining all my functions to include the input parameters in the results?
要回答您的问题(考虑到您对Rx的误解,我很乐意这样做),只有在结果序列的顺序不确定时,才需要这样做.
例如,您一次发出了多个Web请求.
您无法确定他们都会按照您发送给他们的顺序做出回应.
但是,您可以强制这些方案与Concat等运算符的用法保持一致
标签:system-reactive,c 来源: https://codeday.me/bug/20191118/2027972.html