其他分享
首页 > 其他分享> > CodeGo.net>如何关联功能输入和输出?

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