views:

113

answers:

2

I want a method to update certain entries of an IEnumerable. I found that doing a foreach over the entries and updating the values failed as in the background I was cloning the collection. This was because my IEnumerable was backed by some LINQ->SQL queries.

By changing the method to take a List I have changed this behavior, Lists are always mutable and hence the method changes the actual objects in the list. Rather than demand a List is passed is there a Mutable interface I can use?

    // Will not behave as consistently for all IEnumerables
    public void UpdateYesterday (IEnumerable<Job> jobs) {
          foreach (var job in jobs.Where(x => x.date == Yesterday)) {
             job.done = true;
          }
    }

    ...

    public class Job {  
         ...           
         public DateTime Date { get; set; }
    }
+9  A: 

You're not changing the list at all here - the collection itself could be immutable and this code would still "work". You're changing the data within the items in the collection.

Imagine a row of houses - that's pretty immutable (creating or destroying a house is tricky) but you can still change the contents of those houses.

So, what you really need to know is whether the elements of the collection are going to be cloned or not... whether you'll get a collection of "new" jobs each time you perform the query, or whether it'll use the existing objects.

Jon Skeet
I agree the question was poorly worded and my assertions may not be correct. My "Job" class has public set methods however so I can't figure why my original code didn't work if it's not related to container behavior.
Logan
@Logan: It would be a pretty odd sort of container to automatically clone items as they were retrieved - but with a LINQ to SQL query you could potentially see behaviour *like* that.
Jon Skeet
A: 

Here's one hypothetical way this might happen. Say you have a method that takes an existing data source and creates Job objects from that source.

Something like this:

public IEnumerable<Job> GetJobs() {
    var rows = GetRowsFromDatabaseTable();

    foreach (var row in rows)
        yield return new Job(row.Name, row.Date, row.Etc);
}

Then if you had code that did this:

var jobs = GetJobs();

UpdateYesterday(jobs);

foreach (var job in jobs)
    Console.WriteLine(job);

You would find that the jobs you printed using Console.WriteLine did not reflect the updates you performed in UpdateYesterday. This is because UpdateYesterday would have enumerated over the new objects created by GetJobs, and then your foreach loop would have enumerated over a new set of new objects created (again) by GetJobs.

On the other hand, if you simply changed that first line to this:

var jobs = GetJobs().ToList();

Then you would have put all those new objects created by GetJobs into a list, where they would persist, and thus your UpdateYesterday and foreach loop would be referring to the same objects (the ones in the list you created).

Does that make sense? I think this could very well be the problem you're encountering (in which case, the answer is indeed to construct a collection such as a List<Job> in memory from which you can access members to manipulate).

In answer to your question about being able to tell if an IEnumerable is mutable or not, though... well, I really don't think that is possible.

I've removed the old answer, as the new one seems to be what the OP needed.

Dan Tao
Bingo! This is EXACTLY what I'm doing, thanks so much for using your superior powers of telepathy and taking the time to explain what might be the cause and why ToList fixes the problem. I'll update in a few mins with the concrete type being passed to UpdateYesterday.
Logan
@Logan: Ha, nice! Glad to help :)
Dan Tao