HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

pull things from lazy source till timeout is reached

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
pullthingssourcetimeoutreachedlazyfromtill

Problem

I need to pull things from lazy source till timeout is reached in this fashion:

var results = lazySource.SelectMany(item =>
{
    //processing goes here
}).Pull(timeout: TimeSpan.FromSeconds(5), times: maxNumberOfIterations);


and I have this solution:

public static class IEnumerableExtensions
{
    public static IEnumerable Pull(this IEnumerable enumerable, int? times = null)
    {
        if (times == null) 
            return enumerable.ToArray();
        else
            return enumerable.Take(times.Value).ToArray();
    }

    public static IEnumerable Pull(this IEnumerable enumerable, TimeSpan timeout, int? times = null)
    {
        var stopwatch = new Stopwatch();

        stopwatch.Start();

        if (times != null) enumerable = enumerable.Take(times.Value);

        using (var iterator = enumerable.GetEnumerator())
        {
            while (stopwatch.Elapsed < timeout && iterator.MoveNext())
                yield return iterator.Current;
        }
    }
}


Am I just reinvented the wheel and it can be done using some external libs? like Rx/Ix maybe ...

Solution


  • A better name for times would be count.



-
Your two Pull methods are inconsistent:

  • The first one actually returns a copy of the entire input sequence (ToArray()) while the second one doesn't.



  • To keep it consistent with the standard LINQ methods the first method should also just enumerate the input sequence rather than making a copy.



The first method can then be rewritten as:

public static IEnumerable Pull(this IEnumerable enumerable, int? count = null)
{
    return (times == null) ? enumerable : enumerable.Take(count.Value);
}


Or if you prefer to create a new enumerator:

public static IEnumerable Pull(this IEnumerable enumerable, int? count = null)
{
    var items = (times == null) ? enumerable : enumerable.Take(count);
    foreach (var item in items)
    {
        yield return item;
    }
}


-
When i first read this question I assumed the intend was to only wait a maximum time for an item to arrive (i.e. input sequence is possibly blocking). Reading the code however seems that you just want to consume as many items as you can in a given time span (specifically it doesn't deal with blocking input sequences). Assuming the latter is what you wanted then I'd change the code to not use the enumerator interface but stick on a slightly higher level:

public static IEnumerable Pull(this IEnumerable enumerable, TimeSpan timeout, int? count = null)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    var items = (times == null) ? enumerable : enumerable.Take(count.Value);

    foreach (var item in items)
    {
        if (stopwatch.Elapsed >= timeout) { yield break; }
        yield return item;
    }
}

Code Snippets

public static IEnumerable<T> Pull<T>(this IEnumerable<T> enumerable, int? count = null)
{
    return (times == null) ? enumerable : enumerable.Take(count.Value);
}
public static IEnumerable<T> Pull<T>(this IEnumerable<T> enumerable, int? count = null)
{
    var items = (times == null) ? enumerable : enumerable.Take(count);
    foreach (var item in items)
    {
        yield return item;
    }
}
public static IEnumerable<T> Pull<T>(this IEnumerable<T> enumerable, TimeSpan timeout, int? count = null)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    var items = (times == null) ? enumerable : enumerable.Take(count.Value);

    foreach (var item in items)
    {
        if (stopwatch.Elapsed >= timeout) { yield break; }
        yield return item;
    }
}

Context

StackExchange Code Review Q#24492, answer score: 2

Revisions (0)

No revisions yet.