3
\$\begingroup\$

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<T> Pull<T>(this IEnumerable<T> enumerable, int? times = null)
    {
        if (times == null) 
            return enumerable.ToArray();
        else
            return enumerable.Take(times.Value).ToArray();
    }

    public static IEnumerable<T> Pull<T>(this IEnumerable<T> 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 ...

\$\endgroup\$
  • \$\begingroup\$ Use Stopwatch (Elapsed property) instead of DateTime especially DateTime.Now which is slow as hell. \$\endgroup\$ – Peter Kiss Mar 29 '13 at 0:17
  • \$\begingroup\$ Is lazySource under your control? Can you use .Net 4.5? \$\endgroup\$ – svick Mar 29 '13 at 22:15
  • \$\begingroup\$ @svick yes, it is, but it should be lazily evaluated, because data amount could be huge \$\endgroup\$ – IlliakaillI Mar 30 '13 at 0:40
  • \$\begingroup\$ What about my second question? Can you use .Net 4.5? \$\endgroup\$ – svick Mar 30 '13 at 0:57
  • \$\begingroup\$ yes, i can use it \$\endgroup\$ – IlliakaillI Mar 30 '13 at 17:11
2
\$\begingroup\$
  1. A better name for times would be count.
  2. 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<T> Pull<T>(this IEnumerable<T> enumerable, int? count = null)
    {
        return (times == null) ? enumerable : enumerable.Take(count.Value);
    }
    

    Or if you prefer to create a new enumerator:

    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;
        }
    }
    
  3. 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<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;
        }
    }
    
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.