C# 使用可变超时缓冲选定的消息

C# 使用可变超时缓冲选定的消息,c#,system.reactive,C#,System.reactive,我有一个包含字母(a-Z)和数字(1-9)的流。我确实希望加入在超时时间内到达的字母(这可能会改变),并始终立即发出数字。你能建议我哪种功能最好 示例工作代码(不确定这是否正确和/或是一个好的解决方案): private BehaviorSubject sTimeouts=new BehaviorSubject(0.ms()); 私人IObservable信函连接(IObservable ob) { 返回可观察的。创建(观察者=> { 变量字母=新列表(); var lettersFlush=n

我有一个包含字母(a-Z)和数字(1-9)的流。我确实希望加入在超时时间内到达的字母(这可能会改变),并始终立即发出数字。你能建议我哪种功能最好

示例工作代码(不确定这是否正确和/或是一个好的解决方案):

private BehaviorSubject sTimeouts=new BehaviorSubject(0.ms());
私人IObservable信函连接(IObservable ob)
{
返回可观察的。创建(观察者=>
{
变量字母=新列表();
var lettersFlush=new SerialDisposable();
返回ob.Subscribe(c=>
{
如果(字符IsUpper(c))
{
if((等待sTimeouts.FirstAsync()).Ticks>0)
{
添加(c);
一次性的=
可变超时(刺激)
.订阅(x=>{
observer.OnNext(String.Concat(字母));
字母。清除();
});
}
其他的
observer.OnNext(字母.ToString());
}
else if(字符IsDigit(c))
observer.OnNext(c.ToString());
}
}
}
专用IObservable VariableTimeout(IObservable超时)
{
返回可观察的。创建(obs=>
{
var sd=new SerialDisposable();
var first=DateTime.Now;
返回超时
.订阅(超时=>
{
if(timeout.Ticks==0 | | first+timeout{
obs.OnNext(t);
obs.OnCompleted();
});
}
});
});
}
私有无效更改超时(int超时)
{
sTimeouts.OnNext(timeout.ms())
}
//我使用以下扩展方法
公共静态类ticketxtensions
{
公共静态时间跨度毫秒(此整数毫秒)
{
返回时间跨度(毫秒);
}
}
要修改超时,我可以简单地更改private timeout变量,但是如果需要/更好的话,可能可以为它设置一个主题

更新

var scheduler = new TestScheduler();

var timeout = scheduler.CreateColdObservable<int>(
    ReactiveTest.OnNext(0000.Ms(), 2000),
    ReactiveTest.OnNext(4300.Ms(), 1000));

var input = scheduler.CreateColdObservable<char>(
    ReactiveTest.OnNext(0100.Ms(), '1'),
    ReactiveTest.OnNext(1600.Ms(), '2'),
    ReactiveTest.OnNext(1900.Ms(), 'A'),
    ReactiveTest.OnNext(2100.Ms(), 'B'),
    ReactiveTest.OnNext(4500.Ms(), 'C'),
    ReactiveTest.OnNext(5100.Ms(), 'A'),
    ReactiveTest.OnNext(5500.Ms(), '5'),
    ReactiveTest.OnNext(6000.Ms(), 'B'),
    ReactiveTest.OnNext(7200.Ms(), '1'),
    ReactiveTest.OnNext(7500.Ms(), 'B'),
    ReactiveTest.OnNext(7700.Ms(), 'A'),
    ReactiveTest.OnNext(8400.Ms(), 'A'));

var expected = scheduler.CreateColdObservable<string>(
    ReactiveTest.OnNext(0100.Ms(), "1"),
    ReactiveTest.OnNext(1600.Ms(), "2"),
    ReactiveTest.OnNext(4100.Ms(), "AB"),
    ReactiveTest.OnNext(5500.Ms(), "5"),
    ReactiveTest.OnNext(7000.Ms(), "CAB"),
    ReactiveTest.OnNext(7200.Ms(), "1"),
    ReactiveTest.OnNext(9400.Ms(), "BAA"));


// if ReactiveTest.OnNext(3800.Ms(), 1000)
// then expected is ReactiveTest.OnNext(3800.Ms(), "AB")
var scheduler=newtestscheduler();
var timeout=scheduler.CreateColdObservable(
ReactiveTest.OnNext(0000.Ms(),2000),
ReactiveTest.OnNext(4300.Ms(),1000));
var input=scheduler.CreateColdObservable(
ReactiveTest.OnNext(0100.Ms(),'1'),
ReactiveTest.OnNext(1600.Ms(),'2'),
ReactiveTest.OnNext(1900.Ms(),'A'),
ReactiveTest.OnNext(2100.Ms(),'B'),
ReactiveTest.OnNext(4500.Ms(),'C'),
ReactiveTest.OnNext(5100.Ms(),'A'),
ReactiveTest.OnNext(5500.Ms(),'5'),
ReactiveTest.OnNext(6000.Ms(),'B'),
ReactiveTest.OnNext(7200.Ms(),'1'),
ReactiveTest.OnNext(7500.Ms(),'B'),
ReactiveTest.OnNext(7700.Ms(),'A'),
OnNext(8400.Ms(),'A');
var expected=scheduler.CreateColdObservable(
ReactiveTest.OnNext(0100.Ms(),“1”),
ReactiveTest.OnNext(1600.Ms(),“2”),
ReactiveTest.OnNext(4100.Ms(),“AB”),
ReactiveTest.OnNext(5500.Ms(),“5”),
ReactiveTest.OnNext(7000.Ms(),“CAB”),
ReactiveTest.OnNext(7200.Ms(),“1”),
ReactiveTest.OnNext(9400.Ms(),“BAA”);
//if ReactiveTest.OnNext(3800.Ms(),1000)
//然后应该是ReactiveTest.OnNext(3800.Ms(),“AB”)
更新#2


精确的解决方案正确地支持缓冲期间的超时更改

假设
sampleInput
作为示例输入:

var charStream = "12ABCAB1BAA".ToObservable();
var random = new Random();
var randomMilliTimings = Enumerable.Range(0, 12)
    .Select(i => random.Next(2000))
    .ToList();

var sampleInput = charStream
    .Zip(randomMilliTimings, (c, ts) => Tuple.Create(c, TimeSpan.FromMilliseconds(ts)))
    .Select(t => Observable.Return(t.Item1).Delay(t.Item2))
    .Concat();
首先,与其更改可变变量,不如生成一些流来表示缓冲区窗口:

Input:  1---2--A-B----C--A-B-1--B-A--A
Window: ---------*--------*---------*--
Output: 1---2----AB-------CAB-1-----BAA
我生成了一个递增的
TimeSpan
s流,并将其称为
bufferbounders
,如下所示:

var bufferBoundaries = Observable.Range(1, 20)
    .Select(t => Observable.Return(t).Delay(TimeSpan.FromSeconds(t)))
    .Concat();
这看起来像这样:

Seconds: 0--1--2--3--4--5--6--7--8--9--10
BB     : ---1-----2--------3-----------4-
…接下来,您要将
sampleInput
拆分为字母和数字的单独流,并相应地处理它们:

var letters = sampleInput
    .Where(c => char.IsLetter(c))
    .Buffer(bufferBoundaries)
    .Where(l => l.Any())
    .Select(lc => new string(lc.ToArray()));

var numbers = sampleInput
    .Where(c => char.IsNumber(c))
    .Select(c => c.ToString());
接下来,将两个流合并在一起:

var finalOutput = letters.Merge(numbers);
最后,如果您能提供帮助,订阅两次相同的输入通常不是一个好主意(在我们的例子中,
sampleInput
),因此在我们的例子中,我们应该用以下内容替换
字母
数字
、和
finalOutput

var publishedFinal = sampleInput
    .Publish(_si => _si
        .Where(c => char.IsLetter(c))
        .Buffer(bufferBoundaries)
        .Where(l => l.Any())
        .Select(lc => new string(lc.ToArray()))
        .Merge( _si
            .Where(c => char.IsNumber(c))
            .Select(c => c.ToString())
        )
    );

假设
sampleInput
作为示例输入:

var charStream = "12ABCAB1BAA".ToObservable();
var random = new Random();
var randomMilliTimings = Enumerable.Range(0, 12)
    .Select(i => random.Next(2000))
    .ToList();

var sampleInput = charStream
    .Zip(randomMilliTimings, (c, ts) => Tuple.Create(c, TimeSpan.FromMilliseconds(ts)))
    .Select(t => Observable.Return(t.Item1).Delay(t.Item2))
    .Concat();
首先,与其更改可变变量,不如生成一些流来表示缓冲区窗口:

Input:  1---2--A-B----C--A-B-1--B-A--A
Window: ---------*--------*---------*--
Output: 1---2----AB-------CAB-1-----BAA
我生成了一个递增的
TimeSpan
s流,并将其称为
bufferbounders
,如下所示:

var bufferBoundaries = Observable.Range(1, 20)
    .Select(t => Observable.Return(t).Delay(TimeSpan.FromSeconds(t)))
    .Concat();
这看起来像这样:

Seconds: 0--1--2--3--4--5--6--7--8--9--10
BB     : ---1-----2--------3-----------4-
…接下来,您要将
sampleInput
拆分为字母和数字的单独流,并相应地处理它们:

var letters = sampleInput
    .Where(c => char.IsLetter(c))
    .Buffer(bufferBoundaries)
    .Where(l => l.Any())
    .Select(lc => new string(lc.ToArray()));

var numbers = sampleInput
    .Where(c => char.IsNumber(c))
    .Select(c => c.ToString());
接下来,将两个流合并在一起:

var finalOutput = letters.Merge(numbers);
最后,如果您能提供帮助,订阅两次相同的输入通常不是一个好主意(在我们的例子中,
sampleInput
),因此在我们的例子中,我们应该用以下内容替换
字母
数字
、和
finalOutput

var publishedFinal = sampleInput
    .Publish(_si => _si
        .Where(c => char.IsLetter(c))
        .Buffer(bufferBoundaries)
        .Where(l => l.Any())
        .Select(lc => new string(lc.ToArray()))
        .Merge( _si
            .Where(c => char.IsNumber(c))
            .Select(c => c.ToString())
        )
    );

这里有几件事可能会有所帮助

首先,大理石图有助于将问题形象化,但当证明某些东西是否有效时,让我们用
ITestableObservable
实例进行说明和单元测试

第二,我不确定你的解决方案应该是什么。如果我看一下你的大理石图,我会发现一些差异。在这里,我添加了一个时间线来帮助可视化

                 111111111122222222223
Time:   123456789012345678901234567890
Input:  1---2--A-B----C--A-B-1--B-A--A
Output: 1---2----AB-------CAB-1-----BAA 
这里我看到了“AB”o