How to properly check IEnumerable for existing results

What is the best way to check if a collection has items?

Here is an example of what I have:

var terminalsToSync = TerminalAction.GetAllTerminals();

if(terminalsToSync.Any())
    SyncTerminals(terminalsToSync);
else
    GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);

The method GetAllTerminals()will execute the stored procedure and, if we return the result, ( Any()is true) SyncTerminals()will cycle through the elements; thus listing it again and executing the stored procedure a second time.

What is the best way to avoid this?

I would like to find a good solution that can be used in other cases; possibly without converting it to List.

Thanks in advance.

+5
source share
8 answers

How about this, which still cancels execution, but buffers it after execution:

var terminalsToSync = TerminalAction.GetAllTerminals().Lazily();

with:

public static class LazyEnumerable {
    public static IEnumerable<T> Lazily<T>(this IEnumerable<T> source) {
        if (source is LazyWrapper<T>) return source;
        return new LazyWrapper<T>(source);
    }
    class LazyWrapper<T> : IEnumerable<T> {
        private IEnumerable<T> source;
        private bool executed;
        public LazyWrapper(IEnumerable<T> source) {
            if (source == null) throw new ArgumentNullException("source");
            this.source = source;
        }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
        public IEnumerator<T> GetEnumerator() {
            if (!executed) {
                executed = true;
                source = source.ToList();
            }
            return source.GetEnumerator();
        }
    }
}
+3

, , ToArray, Length; , ? , , ...

, SyncTerminals foreach, :

bool any = false;
foreach(var terminal in terminalsToSync)
{
  if(!any)any = true;
  //....
}

if(!any)
  GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);

, if , , .

do...while GetEnumerator; ; , :

var enumerator = terminalsToSync.GetEnumerator();
if(enumerator.MoveNext())
{
  do
  {
    //sync enumerator.Current
  } while(enumerator.MoveNext())
}
else
  GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
+5

- , foreach - , , . null.

, .ToArray(), , :

var terminalsToSync = TerminalAction.GetAllTerminals().ToArray();

if(terminalsToSync.Any())
    SyncTerminals(terminalsToSync);
+3
var terminalsToSync = TerminalAction.GetAllTerminals().ToList();

if(terminalsToSync.Any())
    SyncTerminals(terminalsToSync);
else
    GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
+2

.Length .Count , GetEnumerator()/MoveNext()/Dispose(), Any()

+1

:

int count = SyncTerminals(terminalsToSync);
if(count == 0)  GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);

SyncTerminals:

int count = 0;
foreach(var obj in terminalsToSync) {
    count++;
    // some code
}
return count;

.

+1

, . , , .

:

public class LazyListTest
{
    private int _count = 0;

    public void Test()
    {
        var numbers = Enumerable.Range(1, 40);
        var numbersQuery = numbers.Select(GetElement).ToLazyList(); // Cache lazy
        var total = numbersQuery.Take(3)
            .Concat(numbersQuery.Take(10))
            .Concat(numbersQuery.Take(3))
            .Sum();
        Console.WriteLine(_count);
    }

    private int GetElement(int value)
    {
        _count++;
        // Some slow stuff here...
        return value * 100;
    }
}

Test(), _coun t 10. 16, .ToList() 40!

LazyList .

+1

, GetAllTerminals(), , . , , .

The simplest solution, as you mentioned, is to copy the result of the call before performing any other operations. If you wanted to, you could neatly wrap this behavior in IEnumerable<T>, which makes an internal enumerated call only once:

public class CachedEnumerable<T> : IEnumerable<T>
{
    public CachedEnumerable<T>(IEnumerable<T> enumerable)
    {
        result = new Lazy<List<T>>(() => enumerable.ToList());
    }

    private Lazy<List<T>> result;

    public IEnumerator<T> GetEnumerator()
    {
        return this.result.Value.GetEnumerator();
    }

    System.Collections.IEnumerable GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Wrap the result in an instance of this type and it will not evaluate the internal enumerable multiple times.

0
source

All Articles