2

I have 2 object's let's call them A, B and method

List<B> DoSomething(ref A a, List<B> b)
{
     List<B> newList = new List<B>();

     //
     //Doing something to ref A
     //

     foreach(var elementOfB in b.where(...))
     {
        //
        elementOfB.Name = "...";
        //
        newList.Add(elementOfB);
     }
     return newList;
}

So, after that method is done my original list b have changed value Name field (string) but I did not pass it as ref and I'm using where that should return copy of elemens right? So my question is why my list b changed it's values?

List<B> originalList = ...;
List<B> newList = DoSomething(ref a, originalList);
//now originalList have changed Name field values
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Carlos28
  • 2,381
  • 3
  • 21
  • 36

6 Answers6

4

Actually List<B> is a list of reference to B elements. The List of reference changed, but the references still point to the same objects.

If a clone object is what you need, take a look at this topic.

Community
  • 1
  • 1
Perfect28
  • 11,089
  • 3
  • 25
  • 45
2

You;re actually doing shallow coping: you copy references to objects, without creating clones of objects:

  // Shallow copy (your actual implementation):
  // NewList is not b, but it contains references to objects in b
  var NewList = b
    .Where(item => ...)
    .ToList(); 

  // Deep copy (which probably you're looking for)
  // NewList doesn't contain any reference to any objects in b 
  var NewList = b
    .Where(item => ...)
    .Select(item => item.Clone()) // You need this, proving that Clone() makes a deep copy
    .ToList(); 

If you want to clone the items, you have to implement properly ICloneable interface for B class (to ensure the deep coping)

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • `ICloneable` does not guarantee anything. You should probably clarify to the OP that he will still need to implement *deep copying* of his object. Problem with `ICloneable` is that the caller doesn't know what type of clone you're doing. – Yuval Itzchakov Sep 30 '15 at 09:50
  • @Yuval Itzchakov: thank you, the clarification is really wanted. I've edited the answer. – Dmitry Bychenko Sep 30 '15 at 09:55
1

why my list b changed it's values?

Because although you allocated a new list, both of them now still point to the same B instance. You haven't created a new instance prior to adding them to the new list.

An example of how you could clone an existing object, is via a copy constructor:

public class B
{
    public string Name { get; }
    public B(B other)
    {
        Name = other.name;
    }
}

Now when you want to create the list, you do:

foreach(var elementOfB in b.Where(SomePredicate))
{
    elementOfB.Name = "...";
    newList.Add(new B(elementOfB));
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
0

All types in C# are passed as reference except simple types (int, double etc.). If you want to create a copy you need to do this manually

Dominik S
  • 176
  • 4
  • 18
0

When b.Where(..).ToList then only copy is created.Where returns IEnumerable which allows you to iterate using foreach loop.

Viru
  • 2,228
  • 2
  • 17
  • 28
0
Where(t => IsOk(t));

doesn't create a deep copy of the IEnumerable on which it's called. It iterates over the collection and returns the items matching the isOk() criteria. Meaning it creates a new list but the items are the same.

If you would like to create a copy of the each of the items you should implement ICloneable on the items and Clone them in a new list.

Christo S. Christov
  • 2,268
  • 3
  • 32
  • 57