1

Assuming the following piece of code that caches two collections of objects MyObject: one collection is of type IEnumerable<MyObject> and the other one is of type List<MyObject>. The code retrieves the values from the cache and then accesses the collection:

class Program
{
    static void Main(string[] args)
    {
        CacheManager.CacheSomething();
    }

    public class MyService
    {
        private IEnumerable<AnObject> AnObjects
        {
            get
            {
                return new[]
                {
                    new AnObject {MyString1 = "one", MyString2 = "two"},
                    new AnObject {MyString1 = "three", MyString2 = "four"}
                };
            }
        }

        public IEnumerable<AnObject> GetEnumerable()
        {
            return AnObjects;
        }

        public List<AnObject> GetList()
        {
            // Run it out to a list
            return AnObjects.ToList();
        }
    }

    public static class CacheManager
    {
        public static void CacheSomething()
        {
            // Get service
            var service = new MyService();

            // Get the values as List and Enumerable
            var list = service.GetList();
            var enumerable = service.GetEnumerable();

            // Putting them in a cache
            HttpRuntime.Cache.Insert("list", list);
            HttpRuntime.Cache.Insert("enumerable", enumerable);

            // Get the values
            var retrievedList = HttpRuntime.Cache["list"] as List<AnObject>;
            var retrievedEnumerable = HttpRuntime.Cache["enumerable"] as IEnumerable<AnObject>;

            // Access both
            var res1 = retrievedList.ToList();
            var res2 = retrievedEnumerable.ToList();               
        }
    }

    public class AnObject
    {
        public string MyString1 { get; set; }
        public string MyString2 { get; set; }
    }
}

Is there a difference in terms of the amount of memory required to store these objects based on the collection types?

The reason that I ask is that when we have been profiling our applications and we've noticed that when we look at the dependency tree, the IEnumerable has the Service associated with it. Does that mean that it caches the service too?

Can anyone shed some light as to whether this is a cause for concern? Is it a problem to store an IEnumerable in the cache? Should we prefer caching Lists over IEnumerables?

Luke
  • 22,826
  • 31
  • 110
  • 193
  • 1
    If you've gone through the process of writing up all this, why not go one step further and use a benchmark tool to see how both perform and what they allocate? – Jeroen Vannevel Jan 18 '18 at 17:45
  • I've just spent the best part of my day investigating this and I still don't have a concrete answer. Getting proof of this is harder that you might think. – Luke Jan 19 '18 at 16:21

2 Answers2

3

And IEnumerable is not data. It is a promise that you will receive data when you ask. Some data might implement it (arrays, lists) but sometimes, it's not materialized data but instead a query to a database.

"Caching" your IEnumerable means you cache the knowledge where to get data. That's not what you want. You want to cache the data itself.

Always materialize your IEnumerables before caching the result (for example with ToList or ToArray). Otherwise, you might end up with a cache that just holds a procedure that is called to get you the data.


The fact that the IEnumerable in your example still holds a reference to the service is exactly that: it does not hold data, it holds the reference to the service and will call it again when you use it. So the exact opposite of what you want from the cache.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
0

Difference in amount of memory? No.

Why? IEnumerable is not a type; it's an interface. That means that anything stored in an IEnumerable is actually some other type that implements IEnumerable (like List, for example).

IEnumerable only forces implementation of methods to read the list. It's not appropriate for modifying the collection. That's why you would want to cast it to the actual type (like List), so you can use methods like Add.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Thanks. So in terms of memory usage, if as @nvoigt says, storing a promise of data when asked, if it doesnt store the data itself then I thought it might be different in terms of the memory footprint? (A pointer to a promise of data is different to storing the actual materialised data in the cache?) Thanks for your time and knowledge. – Luke Jan 18 '18 at 19:38