1

Let's say I have the following code:

List<Category> categories = getCategories();
List<Category> unusedCategories = categories;
    foreach (var category in categories)
        foreach (var imageCategory in image.Categories)
            if (category.CategoryID == imageCategory.CategoryID)
                unusedCategories.Remove(category);

I was getting the error that a collection was getting modified during the loop. Sure enough, as I stepped through the debugger, if remove(category) was used the "categories" list was one element shorter than before! Why does removing from "unusedCategories" affect "categories"? They should be two distinct lists, not referencing the same thing. And the .Remove() function passes by value, correct? So how does this happen?

Note: I know there are programmatic alternatives to what I'm doing above and I've already adopted one. I'm just curious why this is happening.

user1442605
  • 192
  • 1
  • 8
  • Possible duplicate of [C# Reference type assignment VS value type assignment](http://stackoverflow.com/questions/1219664/c-sharp-reference-type-assignment-vs-value-type-assignment) – Kash Aug 31 '12 at 18:36

2 Answers2

9

They should be two distinct lists, not referencing the same thing

This is not true. When you assign categories to unusedCategories, you're assigning by reference.

If you want a copy, you need to explicitly make a copy of the list:

List<Category> unusedCategories = new List<Category>(categories);

For a more efficient alternative, you could consider something like:

HashSet<int> usedCategoryIds = new HashSet<int>(image.Categories.Select(c => c.CategoryID));

List<Category> categories = getCategories();
List<Cagegory> unusedCategories = categories.Where(c => !usedCategoryIds.Contains(c => c.CategoryID)).ToList();
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • I see. Why is the assignment by reference in this case, when other assignments (like int y = x) are by value? – user1442605 Aug 31 '12 at 18:40
  • 1
    @user1442605 Because `List` is a reference type (class). Any `class` in C# is a reference type, and assignments are by reference. Any `struct` (like `Int32` or `Double`, or custom structs you make) is a value type, and assigned by value. – Reed Copsey Aug 31 '12 at 18:41
  • Thanks for explaining. And I was always wondering what the difference between a class and a struct was...guess I learned it the hard way. – user1442605 Aug 31 '12 at 18:47
0

You can do this with LINQ, for example:

var removables = from category in categories
                        join imageCategory in image.Categories 
                              on category.CategoryID equals 
                                 imageCategory.CategoryID  select category;

and after remove

 unusedCategories.RemoveAll(removables );

or you can use for loop in order to avoid exception you get.

or follow a path described by @Reed: by detaching reference dependency of both collections.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • Thanks for this, a shame my company makes me target .net 2.0 so I'm out of touch with the beauty of LINQ calls. – user1442605 Aug 31 '12 at 18:44
  • @user1442605: it's not shame, it's business strategy. This happen, if there is some financial convenience, if you would be able to show to marketing that they loose the money, tommorow you will have 4.5 version :) – Tigran Aug 31 '12 at 18:51