Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8530745
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 11, 20262026-06-11T09:19:15+00:00 2026-06-11T09:19:15+00:00

Provided items is the result of a LINQ expression: var items = from item

  • 0

Provided items is the result of a LINQ expression:

var items = from item in ItemsSource.RetrieveItems()
            where ...

Suppose generation of each item takes some non-negligeble time.

Two modes of operation are possible:

  1. Using foreach would allow to start working with items in the beginning of the collection much sooner than whose in the end become available. However if we wanted to later process the same collection again, we’ll have to copy save it:

    var storedItems = new List<Item>();
    foreach(var item in items)
    {
        Process(item);
        storedItems.Add(item);
    }
    
    // Later
    foreach(var item in storedItems)
    {
        ProcessMore(item);
    }
    

    Because if we’d just made foreach(... in items) then ItemsSource.RetrieveItems() would get called again.

  2. We could use .ToList() right upfront, but that would force us wait for the last item to be retrieved before we could start processing the first one.

Question: Is there an IEnumerable implementation that would iterate first time like regular LINQ query result, but would materialize in process so that second foreach would iterate over stored values?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-11T09:19:16+00:00Added an answer on June 11, 2026 at 9:19 am

    A fun challenge so I have to provide my own solution. So fun in fact that my solution now is in version 3. Version 2 was a simplification I made based on feedback from Servy. I then realized that my solution had huge drawback. If the first enumeration of the cached enumerable didn’t complete no caching would be done. Many LINQ extensions like First and Take will only enumerate enough of the enumerable to get the job done and I had to update to version 3 to make this work with caching.

    The question is about subsequent enumerations of the enumerable which does not involve concurrent access. Nevertheless I have decided to make my solution thread safe. It adds some complexity and a bit of overhead but should allow the solution to be used in all scenarios.

    public static class EnumerableExtensions {
    
      public static IEnumerable<T> Cached<T>(this IEnumerable<T> source) {
        if (source == null)
          throw new ArgumentNullException("source");
        return new CachedEnumerable<T>(source);
      }
    
    }
    
    class CachedEnumerable<T> : IEnumerable<T> {
    
      readonly Object gate = new Object();
    
      readonly IEnumerable<T> source;
    
      readonly List<T> cache = new List<T>();
    
      IEnumerator<T> enumerator;
    
      bool isCacheComplete;
    
      public CachedEnumerable(IEnumerable<T> source) {
        this.source = source;
      }
    
      public IEnumerator<T> GetEnumerator() {
        lock (this.gate) {
          if (this.isCacheComplete)
            return this.cache.GetEnumerator();
          if (this.enumerator == null)
            this.enumerator = source.GetEnumerator();
        }
        return GetCacheBuildingEnumerator();
      }
    
      public IEnumerator<T> GetCacheBuildingEnumerator() {
        var index = 0;
        T item;
        while (TryGetItem(index, out item)) {
          yield return item;
          index += 1;
        }
      }
    
      bool TryGetItem(Int32 index, out T item) {
        lock (this.gate) {
          if (!IsItemInCache(index)) {
            // The iteration may have completed while waiting for the lock.
            if (this.isCacheComplete) {
              item = default(T);
              return false;
            }
            if (!this.enumerator.MoveNext()) {
              item = default(T);
              this.isCacheComplete = true;
              this.enumerator.Dispose();
              return false;
            }
            this.cache.Add(this.enumerator.Current);
          }
          item = this.cache[index];
          return true;
        }
      }
    
      bool IsItemInCache(Int32 index) {
        return index < this.cache.Count;
      }
    
      IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
      }
    
    }
    

    The extension is used like this (sequence is an IEnumerable<T>):

    var cachedSequence = sequence.Cached();
    
    // Pulling 2 items from the sequence.
    foreach (var item in cachedSequence.Take(2))
      // ...
    
    // Pulling 2 items from the cache and the rest from the source.
    foreach (var item in cachedSequence)
      // ...
    
    // Pulling all items from the cache.
    foreach (var item in cachedSequence)
      // ...
    

    There is slight leak if only part of the enumerable is enumerated (e.g. cachedSequence.Take(2).ToList(). The enumerator that is used by ToList will be disposed but the underlying source enumerator is not disposed. This is because the first 2 items are cached and the source enumerator is kept alive should requests for subsequent items be made. In that case the source enumerator is only cleaned up when eligigble for garbage Collection (which will be the same time as the possibly large cache).

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I've got a series of items in a MySQL database. Each item has four
I've simple Linq2Sql query: var result = from t in MyContext.MyItems select new MyViewModelClass()
I'm creating a dynamic expression, which will order items in a list by some
I`m realy getting frustrated, I am trying to show some items from a XML
I have a class containing a collection of items. For convenience I've provided GetCurrentItem
I know rails provided CSRF protection by default from rails3. but my web application
Judging from the result of my last inquiry , I need to calculate and
I have a large database of artists, albums, and tracks. Each of these items
I'm trying to make a list of inventory to be counted, provided the item
I have some functions that deal with data provided in array format. These functions

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.