1

I'm trying to perform a deep copy of an object in C# so when I do the following:

Route currentBestRoute = Ants[0].Route;

currentBestRoute would not change after altering Ants[0].Route.

I have tried altering the Route class:

using System;
using System.Collections.Generic;

namespace ACO.Models
{

    public class Route : ICloneable
    {

        public List<City> Cities = new List<City>();
        public string Name
        {
            get
            {
                string name = "";

                for(int i = 0; i < Cities.Count; i++)
                {
                    name += Cities[i].Name;

                    if (i != Cities.Count - 1)
                    {
                        name += "->";
                    }
                }
                return name;

            }
        }
        public double Distance
        {
            get
            {
                double distance = 0.0;

                for(int i = 0; i < Cities.Count - 1; i++)
                {
                    distance += Cities[i].measureDistance(Cities[i + 1]);
                }

                return distance;
            }

        }

        public object Clone()
        {
            Route route = new Route
            {
                Cities = Cities
            };
            return route;
        }
    }
}

and conduct a deep clone as below:

 private static Route GetCurrentBestRoute()
        {
            Route currentBestRoute = (Route) Ants[0].Route.Clone();

            foreach(Ant ant in Ants)
            {
                if(ant.Route.Distance < currentBestRoute.Distance)
                {
                    currentBestRoute = (Route) ant.Route.Clone();
                }
            }

            return currentBestRoute;
        }

But this is not working. currentBestRoute still changes on its own every time the Ants List is updated.

Am I missing something?

drew181
  • 333
  • 1
  • 10
  • https://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-of-an-object-in-net-c-specifically – Drag and Drop Nov 18 '19 at 13:37
  • 1
    You should consider making Route immutable. – H H Nov 18 '19 at 13:38
  • 1
    It should be understodd that 'deep' copy is not a well-defined concept but depends on the depth of nesting of the data.. – TaW Nov 18 '19 at 13:40
  • @HenkHolterman In my project, an object of type Route is constantly being updated, why do you think I should consider making it immutable? – drew181 Nov 18 '19 at 13:42
  • Because then you don't have to Clone them. Update and Replace are both applicable. – H H Nov 18 '19 at 13:43

2 Answers2

2
public object Clone()
{
    Route route = new Route
    {
        //Cities = Cities
          Cities = this.Cities.ToList(),
    };
    return route;
}
H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    This was it. Thanks, although I'd prefer an explanation why Cities = Cities did not work. – drew181 Nov 18 '19 at 13:40
  • 1
    an explanation or, preferrably a close as duplicate vote. – TaW Nov 18 '19 at 13:42
  • 1
    `Cities = this.Cities` makes a shallow copy. Two references to the same List. – H H Nov 18 '19 at 13:42
  • @drew181 although it might seem to you that it worked, please beware that you get only a valid deep copy of the list itself! all the items inside it are still the same references as in the other list! If you change one of the city names in the original, then the corresponding cityname in the copy will also change! this part remains shallow – Mong Zhu Nov 18 '19 at 13:53
  • 1
    @MongZhu - correct. I assumed a City to be immutable (as it should be, much more than a Route). – H H Nov 18 '19 at 13:57
  • @Mong Zhu Oh? I had also implemented ICloneable to City. Does that solve the potential problem? – drew181 Nov 18 '19 at 13:59
  • @drew181 nope, it will not. You need to call `Clone` explicitly something like `Cities = this.Cities.Select(x => (City)x.Clone()).ToList(),` – Mong Zhu Nov 18 '19 at 15:26
  • @drew181 - but why is your City clonable? What to do with 2 copies of London? – H H Nov 18 '19 at 15:44
  • @HenkHolterman either time travel of some multiverse actions... ;) – Mong Zhu Nov 18 '19 at 16:06
  • @HenkHolterman I'm working on a TSP and required to input the same city twice (both as a start and end city) to the same list. However my attempt to make it cloneable was due to my insufficient knowledge of why and how deep copying works. MyCity class is no longer cloneable now. – drew181 Nov 18 '19 at 18:33
0

IConeable interface doesn't create deep copy. you can use [Serializable] attribute on class and use this generic code

public static T DeepClone<T>(T obj)
{
     using (var ms = new MemoryStream())
     {
         var formatter = new BinaryFormatter();
         formatter.Serialize(ms, obj);
         ms.Position = 0;

         return (T) formatter.Deserialize(ms);
      }
}
ARandomCoder
  • 196
  • 5
  • if you don't want to use attributes another approach is to use copy constructor but it takes quite a while. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-write-a-copy-constructor – ARandomCoder Nov 18 '19 at 13:46
  • There are lots of issues with this, performance not being the last one. – H H Nov 18 '19 at 13:49
  • this would be basically [this answer from 2008](https://stackoverflow.com/a/129395/5174469) – Mong Zhu Nov 18 '19 at 13:56