Search code examples
c#ienumerableicollection

How do I add to an ICollection property when iterating object type?


I have need to add to an ICollection<string> property of a class of which I have an IEnumerable of. Here is a complete program which illustrates the problem:

using System;
using System.Collections.Generic;
using System.Linq;

namespace CollectionAddingTest
{
    public class OppDocumentServiceResult
    {
        public OppDocumentServiceResult()
        {
            this.Reasons = new List<string>();
        }

        public Document Document { get; set; }

        public bool CanBeCompleted
        {
            get
            {
                return !Reasons.Any();
            }
        }

        public ICollection<string> Reasons { get; private set; }
    }

    public class Document
    {
        public virtual string Name { get; set; }
    }

    public class Program
    {
        private static void Main(string[] args)
        {
            var docnames = new List<string>(new[] {"test", "test2"});

            var oppDocResult = docnames
                .Select(docName
                        => new OppDocumentServiceResult
                               {
                                   Document = new Document { Name = docName }
                               });

            foreach (var result in oppDocResult)
            {
                result.Document.Name = "works?";
                result.Reasons.Add("does not stick");
                result.Reasons.Add("still does not stick");
            }

            foreach (var result in oppDocResult)
            {
                // doesn't write "works?"
                Console.WriteLine(result.Document.Name);

                foreach (var reason in result.Reasons)
                {
                    // doesn't even get here
                    Console.WriteLine("\t{0}", reason);
                }
            }
        }
    }
}

I would expect that each OppDocumentServiceResult would have its referenced Document.Name property set to works? and each OppDocumentServiceResult should have two reasons added to it. However, neither is happening.

What is special about the Reasons property that I cannot add things to it?


Solution

  • Fixed like this, converting to List instead of keeping the IEnumerable:

    var oppDocResult = docnames
            .Where(docName => !String.IsNullOrEmpty(docName))
            .Select(docName
                => new OppDocumentServiceResult
                {
                    Document = docName
                }).ToList();
    

    I can only guess (this is a shot in the dark really!) that the reason behind this is that in an IEnumerable the elements are like "proxies" of the real elements? basically the Enumerable defined by the Linq query is like a "promise" to get all the data, so everytime you iterate you get back the original items? That does not explain why a normal property still sticks...

    So, the fix is there but the explanation I am afraid is not... not from me at least :(