Consider this snippet (much simplified than the original code):
async IAsyncEnumerable<(DateTime, double)> GetSamplesAsync()
{
// ...
var cbpool = new CallbackPool(
HandleBool: (dt, b) => { },
HandleDouble: (dt, d) =>
{
yield return (dt, d); //not allowed
});
while (await cursor.MoveNextAsync(token))
{
this.Parse(cursor.Current, cbpool);
}
}
private record CallbackPool(
Action<DateTime, bool> HandleBool,
Action<DateTime, double> HandleDouble
);
Then, the below Parse
is just a behavior-equivalent of the original.
Random _rnd = new Random();
void Parse(object cursor, CallbackPool cbpool)
{
double d = this._rnd.NextDouble(); //d = [0..1)
if (d >= 0.5)
{
cbpool.HandleDouble(new DateTime(), d);
}
else if (d >= 0.25)
{
cbpool.HandleBool(new DateTime(), d >= 0.4);
}
}
However, I do like the GetSamplesAsync
code, but the compiler does not: the yield
cannot be used within a lambda.
So, I changed the function as follows, although it became much less readable (and also error-prone):
async IAsyncEnumerable<(DateTime, double)> GetSamplesAsync()
{
// ...
(DateTime, double) pair = default;
bool flag = false;
var cbpool = new CallbackPool(
HandleBool: (dt, b) => { },
HandleDouble: (dt, d) =>
{
pair = (dt, d);
flag = true;
});
while (await cursor.MoveNextAsync(token))
{
this.Parse(cursor.Current, cbpool);
if (flag)
{
yield return pair;
}
flag = false;
}
}
I wonder if there is a better way to solve this kind of pattern.
The external flag
/pair
is pretty dangerous and unnecessary (and it forces a closure); it seems like this bool
could be returned from the Parse
method, for example:
await foreach (var item in cursor)
{
if (Parse(item, cbpool, out var result))
yield return result;
}
(everything could also be returned via a value-tuple if you don't like the out
)