7

Ok this is a little bit hard to ask, but i will try. I have a List with objects (Lots), which contains again a list with objects (Wafers). When i change a value within a wafer it will be changed in both lists! Thats what i want. But when i want to remove a wafer from the copied list, it should not be removed from the orginal list. So i want to have a new List of wafers in each lot but the references to the wafers should be the same as in the original lot because i want to change values to the wafers and it should change the values in the orginal wafer and copied wafer. Is it possible without deep copy?

I have the following code, to explain it better:

    public class Lot
    {
        public string LotName { get; set; }

        public List<Wafer> Wafers { get; set; }
    }

    public class Wafer
    {
        public string WaferName { get; set; }

    }
    [Test]
    public void ListInListTest()
    {
                    //Some Testdata

        List<Lot> lotList = new List<Lot>();
        Lot lot = new Lot();
        lot.LotName = "Lot1";
        lot.Wafers = new List<Wafer>();
        Wafer wafer = new Wafer();
        wafer.WaferName = "Wafer1";
        lot.Wafers.Add(wafer);
        wafer = new Wafer();
        wafer.WaferName = "Wafer2";
        lot.Wafers.Add(wafer);
        wafer = new Wafer();
        wafer.WaferName = "Wafer3";
        lot.Wafers.Add(wafer);
        wafer = new Wafer();
        wafer.WaferName = "Wafer4";
        lot.Wafers.Add(wafer);
        lotList.Add(lot);

        lot = new Lot();
        lot.LotName = "Lot1";
        lot.Wafers = new List<Wafer>();
        wafer = new Wafer();
        wafer.WaferName = "Wafer1";
        lot.Wafers.Add(wafer);
        wafer = new Wafer();
        wafer.WaferName = "Wafer2";
        lot.Wafers.Add(wafer);
        wafer = new Wafer();
        wafer.WaferName = "Wafer3";
        lot.Wafers.Add(wafer);
        wafer = new Wafer();
        wafer.WaferName = "Wafer4";
        lot.Wafers.Add(wafer);
        lotList.Add(lot);

         //Copy the List
        List<Lot> copyList = CopyList(lotList);

        //That works. It removes the lot just in the copyList but not in 
        //the original one
        copyList.RemoveAt(1);

        //This works not like i want. It removes the wafers from the copied list
        //and the original list. I just want, that the list will be changed
        //in the copied list
        copyList[0].Wafers.RemoveAt(0);


    }

    private List<Lot> CopyList(List<Lot> lotList)
    {
        List<Lot> result = new List<Lot>(lotList);

        foreach (Lot lot in result)
        {
            lot.Wafers = new List<Wafer>(lot.Wafers);
        }

        return result;
    }

I hope it is not so confusing? And i hope my question is explained good enough.

svick
  • 236,525
  • 50
  • 385
  • 514
Roman L.
  • 869
  • 1
  • 8
  • 15
  • you cany try out this http://stackoverflow.com/questions/1582285/how-to-remove-elements-from-a-generic-list-while-iterating-over-it – Akshay Joy May 24 '13 at 10:42
  • You can try to serialize and then deserialize without a deep copy – Saravanan May 24 '13 at 10:43
  • 1
    Are you sure that it doesn't work? It seems you're doing it right: create a new list of wafers (that's important) which is populated by existing references. Should work. – JeffRSon May 24 '13 at 10:56
  • No you can't. After all the wafer references aren't copied unless you do deep copy in "CopyList". – treehouse May 24 '13 at 14:00

5 Answers5

7

I think I can see what your issue is here. In your CopyList you effectively clone the list i.e.

lot.Wafers = new List<Wafer>(lot.Wafers);

However, the key point to note here is the reference to the Lot object is still the same - so effectively you are cloning & replacing the Wafers property on the original list as well.

When you call

copyList.RemoveAt(1);

that's fine because you are manipulating a copy of the Lot list. However, when you call

copyList[0].Wafers.RemoveAt(0);

you are modifying the Lot instance which is still being referenced by both lists. To solve the problem you would need to clone the Lot object itself to effectively change the reference i.e.

List<Lot> result = new List<Lot>(lotList.Count);
foreach (Lot item in lotList)
{
    result.Add(new Lot()
    {
        LotName = item.LotName,
        Wafers = new List<Wafer>(item.Wafers)
    });
}

As a result you would lose the automatic update in both lists for the Lot object itself but you would still retain it for modifying the individual Wafer objects (as the reference for them wouldn't have changed).

So to summarise, what you are effectively asking is:

How can I keep the same object reference whilst having a different property reference?

The answer to that question is - you can't.

James
  • 80,725
  • 18
  • 167
  • 237
4

As far as I can see, there is no out-of-the-box way for you to do what you want. Here is a brief description why:

The List<Lot> (far left-hand-side) contains a reference to the Lot object, which in turn contains references to both its member variables.

enter image description here

If you want changes to the LotName and constituents of the Wafers list to be propagated as you describe, the easiest solution is to share a reference to the Lot object between lists:

enter image description here

Now you can see why it is impossible to modify the items in the list of Wafers independently using this solution - you can't get away from the fact that you are using the same object reference!

By sharing a reference you lose the ability to control how differences in behaviour. To achieve what you want, I think you have to do a deep copy of the Lot instances, and you could then manage the propagation of changes through some sort of Observer pattern.

Lawrence
  • 3,287
  • 19
  • 32
  • Wow, thank you. That make things more clear for me! So when i call "new List()" it will just make a new List for the same lot reference. OK.. than i have to do deep copy. :/ – Roman L. May 24 '13 at 12:33
0

The problem is that you create the clone list by maintaining a reference with the main list. You should use something like:

private List<Lot> CopyList(List<Lot> list)
{
   List<Lot> clone = new List<Lot>();

   foreach(Lot l in list)
   {
      List<Wafer> wafers = new List<Wafer>();
      foreach(Wafer w in l.Wafers)
         wafers.Add(w);

      clone.Add(new Lot(){ LotName = l.LotName, Wafers = wafers });
   }

   return clone;
}
Omar
  • 16,329
  • 10
  • 48
  • 66
  • No new Wafer's shall be created, it seems. Only the references need to be kept. – JeffRSon May 24 '13 at 10:55
  • Nope, he doesn't need the reference to _Wafers_, because as you can see he wants to modify the copied list without producing an effect on the main list. – Omar May 24 '13 at 11:00
  • There's no point in creating new Wafers if it's sufficient to copy the reference. – JeffRSon May 24 '13 at 11:01
  • I want to have the SAME wafers (same reference). But the list of wafers should be a NEW reference per lot. So the size of the waferlist can be different for example.. So when i remove a wafer from the copied list it should NOT remove it from the original list. But when i change a value of any wafer it should also change the value for the wafer in the corresponding list. What you tried is a deepcopy. But i don't want to have a deep copy, because i want to change values in a wafer once for all the wafers with the same reference. Yes i know..^^ ... a little bit confusing – Roman L. May 24 '13 at 11:53
0

Short answer: yes it's possible. However, the CopyList method should look like this

private List<Lot> CopyList(List<Lot> lotList)
{
    List<Lot> result = new List<Lot>();

    for (int i = 0; i < lotList.Count; i++)
    {
         Lot lot = new Lot();
         lot.LotName = lotList[i].LotName;
         lot.Wafers = new List<Wafer>(lotList[i].Wafers);
         result.Add(lot);
    }

    return result;
}

This will create a new lotlist and add all existing Wafers to it.

JeffRSon
  • 10,404
  • 4
  • 26
  • 51
  • But it doesn't work the way i did. When i remove a wafer from the copied list it removes it also from the original list :(... – Roman L. May 24 '13 at 11:53
  • It doesn't work. If you copy/paste it in a console application you'll see the described behaviour. – nmat May 24 '13 at 12:21
  • @JeffRSon as far as I can see...it's not. – James May 24 '13 at 12:32
  • Okay - yes. The problem is, that the original lists are used. There is a new copy of Wafers, but not a new lotlist. I overlooked that, obviously. Will edit the answer. – JeffRSon May 24 '13 at 12:35
  • See new version of CopyList – JeffRSon May 24 '13 at 12:42
  • @JeffRSon this answer is still wrong, it's not possible to have the same reference (`Lot` object) in two lists but have 2 different property (`Wafers`) references. The only solution is to clone the list and keep the objects in sync using some other means (e.g. subject/observer pattern). – James May 24 '13 at 13:13
  • @James: The first line in the for loop creates a new Lot list - so what do you mean? It IS another list and it is added to the copied list (result). – JeffRSon May 24 '13 at 13:17
  • @JeffRSon your newly proposed solution does as what I have also suggested in my answer - however, you still seem to suggest that what the OP is asking for can be done but using your code - which it can't. When the `Lot` objects are cloned the reference from the original list is lost so updating a `Lot` object in the copy list **will no longer** update the same object in the original list. The OP want's to be able to retain the `Lot` instance across both lists but maintain a separate `Wafers` property per list which can't be done. – James May 24 '13 at 14:19
  • Unless I misread it again, it is the other way around. "So i want to have a new List of wafers in each lot but the references to the wafers should be the same as in the original lot" - so the OP is aware of having different lots and different list of wafers, but the *same* wafers. His question was not about further synchronizing the original and copy of lots, but about removing Wafers without affecting the original. Anyway - the discussion moves away from the actual problem. – JeffRSon May 24 '13 at 14:34
  • I was focusing more on the "*Is it possible without deep copy?*" to which the answer is no, the `Lot` object needs to be cloned along with the property itself. What the OP is really asking is "*can I have 1 `Lot` object, in 2 separate lists, which reference different `Wafer` lists?*" which isn't possible. – James May 24 '13 at 14:50
0

You should use two separate variables for the lists, e.g. ListLotA and ListLotB. If you want to be able to remove a Wafer from one list without removing it from another, and at the same time adjust a Wafer so that it reflects in both lists, you should copy the Wafers from one list to the other, i.e. by use of a temporary array.

The example code below illustrates this:

List<Lot> ListLotA = new List<Lot>();

Wafer w1 = new Wafer() { WaferName = "w1" };
Wafer w2 = new Wafer() { WaferName = "w2" };
Wafer w3 = new Wafer() { WaferName = "w3" };

ListLotA.Wafers.Add(w1);
ListLotA.Wafers.Add(w2);
ListLotA.Wafers.Add(w3);

List<Lot> ListLotB = new List<Lot>();

// At this point adding w1, w2, w3 to the second list is possible, but let's
// assume it's not and you have to copy them from the first list.
Wafer[] wArray = new Wafer[ListLotA.Wafers.Count];
ListLotA.Wafers.CopyTo(wArray);

ListLotB.Wafers.AddRange(wArray);

ListLotA and ListLotB are separate objects, e.g. they have different references, but the list of individual Wafers have the same reference.

ListLotB.Wafers[1].WaferName = "New Value";

The above line would change the Wafer at index 1 in ListLotB, but it would also effect the same Wafer in ListLotA (despite its index).

ListLotA.Wafers.Remove(w2);

This would remove Wafer w2 from ListLotA, but not from ListLotB.

John Willemse
  • 6,608
  • 7
  • 31
  • 45