Search code examples
c#readonlydeep-copy

Make a deep copy of an object in C# with private readonly variables and objects


This is a follow up question to my previous one, Make a deep copy of an object inside a ConcurrentDictionary in C#. I want to clone my Patient object so that the object PatientCovariates is the same for the copy.

But when I do that, I also want to regenerate the Random Crn to the same object that was created when the Patient struct was constructed by using _seed.

At the same time, I would like to keep Crn readonly and _seed private and readonly. Is this possible?

using System.Collections.Concurrent;
using System.Text.Json;
namespace ExampleCode;
internal class Program
{
    private static void Main(string[] args)
    {
        Exampel obj = new();
        obj.DoStuff();
    }
}

internal class Exampel()
{
    /// <summary>
    /// For the baseline patients (patient ID, patient object), same for all arms and only generated once
    /// </summary>
    private ConcurrentDictionary<int,
                                 Patient> _baselinePatients = new();

    /// <summary>
    /// Patients in the current arm (patient ID, patient object)
    /// </summary> 
    private ConcurrentDictionary<int,
                                 Patient> _patientsCurrentArm = new();

    public void DoStuff()
    {
        const int EXAMPLE_CYCLE = 0;
        // Set the number of arms and treatments
        int _nArms = 2;
        int _nPatients = 5;

        // Add patients at baseline to _baselinePatients
        for (int i = 0; i < _nPatients; i++) {
            Patient patient = new Patient(1);
            patient.PatientCovariates.Add(0, new());
            _baselinePatients.TryAdd(i, patient);
        }

        // Try to copy the patients created in the baseline ConcurrentDictionary
        for (int i = 0; i < _nArms; i++) {
            for (int j = 0; j < _nPatients; j++) {
                Patient patObjCopy = (Patient)_baselinePatients[j].Clone();
                Patient patObjCopyTwo = (Patient)patObjCopy.Clone();
                patObjCopy.PatientCovariates[EXAMPLE_CYCLE].Add("Covariate for patient ",    // To mark the arm name and make the patient struct different
                                                                j.ToString() + 
                                                                " in arm " + 
                                                                i.ToString());

                // Save the patient copy in the _patientsCurrentArm WITHOUT changing the patient in _baselinePatients
                _patientsCurrentArm.TryAdd(j, patObjCopy);
            }
        }
    }

    internal struct Patient : ICloneable
    {
        private readonly int _seed; // used when the object is copied to regenerated the Crn
        public readonly  Random Crn { get; }

        /// <summary>
        /// Cycle, covariate name, covariate value
        /// </summary>
        public Dictionary<int,
                          Dictionary<string,
                                     string>> PatientCovariates
        { get; set; }


        /// <summary>
        /// Initiates PatientCovariates
        /// </summary>
        public Patient(int seed)
        {
            _seed = seed;
            Crn = new(_seed);
            PatientCovariates = new();
        }

        /// <summary>
        /// Clone patients
        /// </summary>
        /// <returns></returns>
        public readonly object Clone()
        {
            Patient patient = new() {
                // Here, I also want to regenerate the Crn by using _seed, as was done when the patient was constructed

                PatientCovariates = this.PatientCovariates.ToDictionary(
                    entry => entry.Key,
                    entry => entry.Value.ToDictionary(
                        innerEntry => innerEntry.Key,
                        innerEntry => innerEntry.Value
                    )
            )
            };
            return patient;
        }
    }
}

Solution

  • Thanks to @Charlieface, I found a solution. I can just use the existing constructor and make Random Crn being readonly.

    public readonly object Clone()
    {
        Patient patient = new(_seed) {
            PatientCovariates = this.PatientCovariates.ToDictionary(
                entry => entry.Key,
                entry => entry.Value.ToDictionary(
                    innerEntry => innerEntry.Key,
                    innerEntry => innerEntry.Value
                )
        )
        };
        return patient;
    }