Search code examples
c#hill-climbing

How do I copy an object by value instead of by reference in C#


I am writing a simple hillclimber algorithm in C# and am trying the following approach:

Get initial Solution
Copy solution to new object
Apply algorithm to copy
Compare obj value of initial solution with obj value of copy
if better - copy back into initial solution.

I am aware of the fact that this topic has been touched on in a previous post and tried implementing the suggestion there - Using the IClonable class. This is what I have tried:

My Solution Class:

class Solution : ICloneable
{

    public Dictionary<Room, List<Patient>> solution { get; set; }


    public Solution()
    {
        solution = new Dictionary<Room, List<Patient>>();

    }
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

The algorithm:

public static void swap (Solution solution, Output input, Random rand)
    {


        Solution intSoln = new Solution();
        intSoln = (Solution)solution.Clone();

//Moving things around in intSoln here

        Console.WriteLine("new solution = " + objValue(intSoln, input));
        Console.WriteLine("old solution = " + objValue(solution, input));
        if (objValue(intSoln, input) < objValue(solution, input))
        {
            solution = (Solution)intSoln.Clone();
        }
    }

Looking at the printouts for the new and old solutions, they are always the same, which means that obviously the code still copies by reference. I am quite stuck and not sure what to do. Any help would be much appreciated.


Solution

  • MemberwiseClone will do a shallow copy, which means you'll get a new instance, whose fields have the same references as the initial object.

    You want to do a deep copy instead. Here's a copy with one more level:

    public object Clone()
    {
        var copy = new Solution();
    
        foreach (var pair in solution)
            copy.solution.Add(pair.Key, pair.Value);
    
        return copy;
    }
    

    This will copy the dictionary, but its keys and values will still point to the same instances. So you may perform a deeper copy with something like that:

    public object Clone()
    {
        var copy = new Solution();
    
        foreach (var pair in solution)
            copy.solution.Add(new Room(pair.Key), pair.Value.ToList());
    
        return copy;
    }
    

    Or:

    public object Clone()
    {
        var copy = new Solution();
    
        foreach (var pair in solution)
            copy.solution.Add(new Room(pair.Key), pair.Value.Select(i => new Patient(i)).ToList());
    
        return copy;
    }
    

    You need to write some copy constructors for Room and Patient for that. Hopefully you get the idea.