2

Hello there :) I'm having a problem when cloning an object that contains other objects and lists in winForm. I want to Clone an Object of Type Structure the classes are provided below first of all i have tried Shallow Clone

public Structure Clone()
{
    return this.MemberwiseClone() as Structure;
}

put it didn't work so i searched https://stackoverflow.com/ and i did a deep clone

Deep cloning objects and this How do you do a deep copy of an object in .NET (C# specifically)?

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);
 }
}

The Problem that I'm having is i am taking a backup copy of an object

var backup=DeepClone<Structure>(this.sched); //sched is Structure Object

and then changing this.sched the backup is changed :(

the structre of my classes is:

[Serializable]
public class Sub
{
    string name;

    int studentsNumber;

    int unassaignedStudent;

    public Sub(string name, int number)
    {
        this.name = name;
        this.studentsNumber = number;
        this.unassaignedStudent = number;
    }

public Subject()
        {
            this.name = "";
            this.studentsNumber = 0;
            this.unassaignedStudent = 0;
        }

    public bool Assigne(int count)
    {
        //stuff
    }

    public Sub Clone()
    {
        return this.MemberwiseClone() as Sub;
    }
}

[Serializable]
class Room
{

    string name;
    int studentsNumber;
    int full;
    private int freeSeats;
    List<Sub> subjects;

    /// <summary>
    /// the list of subjects
    /// </summary>
    internal List<Sub> Subjects
    {
        get { return subjects; }
        set { subjects = value; }
    }

    Dictionary<Subject, int> variations;


    public Room(string name, int number)
    {
        this.name = name;
        this.studentsNumber = number;
        this.full = 0;
        this.subjects = new List<Subject>();
        this.variations = new Dictionary<Subject, int>();
        this.freeSeats = number;
    }

    public Room(int number)
    {
        this.studentsNumber = number;
        this.full = 0;
        this.subjects = new List<Subject>();
        this.variations = new Dictionary<Subject, int>();
        this.freeSeats = number;
    }

     public Room()
    {
        this.name = "R" + count.ToString();
        count++;
        this.studentsNumber = 0;
        this.full = 0;
        this.subjects = new List<Sub>();
        this.variation= new Dictionary<Sub, int>();
        this.freeSeats = 0;
    }


    public bool addSubject(Sub sbj)
    {
         //also stuff
    }

    public bool addPartialSubject(Sub sbj)
    {
           //stuff
           // return false;
    }

    public Room Clone()
    {
        return this.MemberwiseClone() as Room;
    }


}

[Serializable]
class Period
{
    List<Room> rooms;


    int conflicts;


    List<Sub> subjects;
    internal List<Sub> Subjects
    {
        get { return subjects; }
        set { subjects = value; }
    }


    /// <summary>
    /// Create an instance of class Period
    /// </summary>
    /// <param name="rooms">the rooms in this Period</param>
    public Period(List<Room> rooms)
    {
        this.conflicts = 0;
        this.rooms = rooms;
        subjects = new List<Subject>();
        fillSubjects(ref rooms, ref subjects);
    }
     public Period()
    {
        this.conflicts = 0;
        this.rooms = null;
        subjects = new List<Subt>();
        freeSeats = 0;
    }

    /// <summary>
    /// Fill the subjects in the rooms to the list of subjects
    /// </summary>
    /// <param name="rooms">referance to the list of the rooms</param>
    /// <param name="subjects">referance to the list of the subjects</param>
    private void fillSubjects(ref List<Room> rooms, ref List<Sub> subjects)
    {
        foreach (var room in rooms)
        {
            foreach (var subject in room.Subjects)
            {
                if (!subjects.Exists(s => s.Name == subject.Name))
                    subjects.Add(subject);
            }
        }
    }

    /// <summary>
    /// Adds the given subject to the period if there is a place in any room
    /// </summary>
    /// <param name="s">the subject to add</param>
    /// <returns>true if there is space for this subject and added, otherwise false</returns>
    public bool AddSubject(Sub s)
    {
        foreach (var room in rooms)
        {
            if (room.addSubject(s))
            {
                //stuff
            }
            else
                if (room.addPartialSubject(s))
                {
                    //stuff
                }
        }
        return false;
    }

    private int CalculateConflictions(Sub s)
    {
        //also a lot of stuff 
    }
}

[Serializable]
class Structure:IDisposable
{
    int days;

    /// <summary>
    /// the number of days in the Schedual
    /// </summary>
    public int Days
    {
        get { return days; }
        set { days = value; }
    }

    int periods;


    Period[,] schedualArray;

    List<Room> rooms;

    internal List<Room> Rooms
    {
        get { return rooms; }
        set { rooms = value; }
    }

    /// <summary>
    /// Creates an instance of the Structure object
    /// </summary>
    /// <param name="rooms">a list of the rooms in the Schedual</param>
    public Structure(int days, int periods,List<Room> rooms)
    {
        this.days = days;
        this.periods = periods;
        this.rooms=rooms;
        this.schedualArray = new Period[days, periods];
        this.subjectCount = 0;
        for (int i = 0; i < days; i++)
        {
            for (int j = 0; j < periods; j++)
            {
                schedualArray[i, j] = new Period(CloneList(ref rooms)); //here i cloned the list to be in the safe side and it didn't work also
            }
        }
    }

    public Structure()
    {
        this.days = 0;
        this.totalEval = Int32.MaxValue;
        this.periods = 0;
        this.rooms = null;
        this.subjectCount = 0;
        this.schedualArray = null;
    }

    internal bool AddSubject(Sub subject, int day, int period)
    {
        //add the subject into inner lists (room)
    }

    public void PrintStruct()
    {
        for (int i = 0; i < days; i++)
        {
            for (int j = 0; j < periods; j++)
            {
                foreach (var subject in schedualArray[i, j].Subjects)
                {
                    Console.Write("\t\t");
                }
                Console.Write("\t\t");
            }
            Console.WriteLine();
        }
    }

    public Structure Clone()
    {
        return this.MemberwiseClone() as Structure;
    }

    public List<Room> CloneList(ref List<Room> rooms)
    {
        var lst = new List<Room>();
        foreach (var room in rooms)
        {
            lst.Add(DeepClone<Room>(room));
        }
        return lst;
    }

    internal void RemoveSubject(Sub subject)
    {
        //..................
    }

    #region IDisposable Members

    public void Dispose()
    {

        GC.Collect(g, GCCollectionMode.Forced);
    }

    #endregion
}

I don't really now what are the details of the cloning process but my requirement is to clone the entire object with the successive objects in the structure and hirarachy of the object and the complete items that the list points to not only there references Please Can anyone help me please I'm really lost:(, thanks in advance to everyone interested in helping me :) Yaser

EDIT when using the method @WiiMaxx

Structure backup=XamlClone(this.sched);

private Structure XamlClone(Structure s)
{
   //First Step
            // create a XAML string
            string stCopie = XamlWriter.Save(s);

            //Secound Step
            // convert him back to an Object of YourTyp
            return XamlReader.Load(XmlReader.Create(new StringReader(stCopie))) as Structure;
}

but the inner period[,] is cloned as null Can you please help me @WiiMaxx

Edit 2.0 i have also checked this article about deep cloning in 3 methods and i think i have circular references in my code that is why this isn't working for me http://blog.nuclex-games.com/mono-dotnet/fast-deep-cloning/ End Edit

Community
  • 1
  • 1
Yaser Jaradeh
  • 322
  • 1
  • 6
  • 27
  • 1
    Having the code available like that is good. Now, please describe the precise requirements for the clone operation, successively stepping into the object hierarchy. – Pieter Geerkens Mar 16 '13 at 19:13
  • @PieterGeerkens I just updated the footer of my question i hope that i covered your idea :) – Yaser Jaradeh Mar 16 '13 at 20:05
  • I don't believe you :) can you please add a small program that shows the unexpected behavior, i.e. creates an object, clones it, modifies the original and sees the clone change (E.g. using Console.Writeline to write out some state from both objects). Also: you shouldn't forcibly GC things in Dispose. It doesn't do anything useful really. – Anders Forsgren Mar 17 '13 at 11:23
  • i have been reading around the web and i come up a paradigm (circular references) does my code have this ???? can anyone help me????? – Yaser Jaradeh Mar 18 '13 at 21:29

2 Answers2

2

Ok now it should really work
(i deleted also my old solution because it was totally wrong for your needs)

Sub.cs

public class Sub: ICloneable
{
    struct datenStruct
    {
        internal int studentsNumber;
        internal int unassaignedStudent;
        internal string name;
    }

    private datenStruct USE;

    int studentsNumber;

    public string Name
    {
        get { return USE.name; }
        set { USE.name = value; }
    }

    private Sub(datenStruct struc)
    {
        this.USE = struc;
    }

    public Sub(string name, int number)
    {
        this.USE = new datenStruct();
        this.USE.name = name;
        this.USE.studentsNumber = number;
        this.USE.unassaignedStudent = number;
    }

    public bool Assigne(int count)
    {
        //stuff
        return true;
    }

    public object Clone()
    {
        var copie = new datenStruct();

        copie.name = USE.name;
        copie.unassaignedStudent = USE.unassaignedStudent;
        copie.studentsNumber = USE.studentsNumber;

        return new Sub(copie);
    }
}

Room.cs

public class Room: ICloneable
{
    struct datenStruct
    {
        internal int studentsNumber;
        internal int full;
        internal string name;
        internal int freeSeats;
        internal List<Sub> subjects;
        internal Dictionary<Sub, int> variations;
    }

    private datenStruct USE;

    /// <summary>
    /// the list of subjects
    /// </summary>
    internal List<Sub> Subjects
    {
        get { return USE.subjects; }
        set { USE.subjects = value; }
    }

    public Room(string name, int number)
    {
        this.USE = new datenStruct();

        this.USE.name = name;
        this.USE.studentsNumber = number;
        this.USE.full = 0;
        this.USE.subjects = new List<Sub>();
        this.USE.variations = new Dictionary<Sub, int>();
        this.USE.freeSeats = number;
    }

    public Room(int number)
    {
        this.USE = new datenStruct();

        this.USE.studentsNumber = number;
        this.USE.full = 0;
        this.USE.subjects = new List<Sub>();
        this.USE.variations = new Dictionary<Sub, int>();
        this.USE.freeSeats = number;
    }

    private Room(datenStruct struc)
    {
        USE = struc;
    }

    public bool addSubject(Sub sbj)
    {
        //also stuff
        return false;
    }

    public bool addPartialSubject(Sub sbj)
    {
        //stuff
        return false;
    }

    public object Clone()
    {
        var copie = new datenStruct();

        copie.studentsNumber = USE.studentsNumber;
        copie.full = USE.full;
        copie.freeSeats = USE.freeSeats;
        var SubListCopie = new List<Sub>();

        foreach (Sub origSub in USE.subjects)
            SubListCopie.Add((Sub)origSub.Clone());

        copie.subjects = SubListCopie;

        var SubDictCopie = new Dictionary<Sub, int>();

        foreach (KeyValuePair<Sub, int> KvP in USE.variations)
            SubDictCopie.Add((Sub)KvP.Key.Clone(),KvP.Value);

        copie.variations = SubDictCopie;

        return new Room(copie);
    }
}

Period.cs

public class Period: ICloneable
{
    struct datenStruct
    {
        internal List<Room> rooms;
        internal List<Sub> subjects;
        internal string name;
        internal int conflicts;
    }

    private datenStruct USE;


    internal List<Sub> Subjects
    {
        get { return USE.subjects; }
        set { USE.subjects = value; }
    }


    /// <summary>
    /// Create an instance of class Period
    /// </summary>
    /// <param name="rooms">the rooms in this Period</param>
    public Period(List<Room> rooms)
    {
        this.USE.conflicts = 0;
        this.USE.rooms = rooms;
        this.USE.subjects = new List<Sub>();
        fillSubjects(ref USE.rooms, ref USE.subjects);
    }

    private Period(datenStruct struc)
    {
        USE = struc;
    }


    /// <summary>
    /// Fill the subjects in the rooms to the list of subjects
    /// </summary>
    /// <param name="rooms">referance to the list of the rooms</param>
    /// <param name="subjects">referance to the list of the subjects</param>
    private void fillSubjects(ref List<Room> rooms, ref List<Sub> subjects)
    {
        foreach (var room in rooms)
        {
            foreach (var subject in room.Subjects)
            {
                if (!subjects.Exists(s => s.Name == subject.Name))
                    subjects.Add(subject);
            }
        }
    }

    /// <summary>
    /// Adds the given subject to the period if there is a place in any room
    /// </summary>
    /// <param name="s">the subject to add</param>
    /// <returns>true if there is space for this subject and added, otherwise false</returns>
    public bool AddSubject(Sub s)
    {
        foreach (var room in USE.rooms)
        {
            if (room.addSubject(s))
            {
                //stuff
            }
            else
                if (room.addPartialSubject(s))
                {
                    //stuff
                }
        }
        return false;
    }

    private int CalculateConflictions(Sub s)
    {
        //also a lot of stuff 
        return 1;
    }

    public object Clone()
    {
        var copie = new datenStruct();

        copie.name = USE.name;
        copie.conflicts = USE.conflicts;

        var RoomListCopie = new List<Room>();

        foreach (Room origSub in USE.rooms)
            RoomListCopie.Add((Room)origSub.Clone());

        copie.rooms = RoomListCopie;


        var SubListCopie = new List<Sub>();

        foreach (Sub origSub in USE.subjects)
            SubListCopie.Add((Sub)origSub.Clone());

        copie.subjects = SubListCopie;

        return new Period(copie);
    }
}

Structure.cs

public class Structure : IDisposable,ICloneable
{
    struct datenStruct
    {
        internal int days;
        internal int subjectCount;
        internal int periods;
        internal Period[,] schedualArray;
        internal List<Room> rooms;
    }

    private datenStruct USE;



    /// <summary>
    /// the number of days in the Schedual
    /// </summary>
    public int Days
    {
        get { return USE.days; }
        set { USE.days = value; }
    }


    internal List<Room> Rooms
    {
        get { return USE.rooms; }
        set { USE.rooms = value; }
    }



    /// <summary>
    /// Creates an instance of the Structure object
    /// </summary>
    /// <param name="rooms">a list of the rooms in the Schedual</param>
    public Structure(int days, int periods, List<Room> rooms)
    {
        this.USE.days = days;
        this.USE.periods = periods;
        this.USE.rooms = rooms;
        this.USE.schedualArray = new Period[days, periods];
        this.USE.subjectCount = 0;
        for (int i = 0; i < days; i++)
        {
            for (int j = 0; j < periods; j++)
            {
                USE.schedualArray[i, j] = new Period(CloneList(ref rooms)); //here i cloned the list to be in the safe side and it didn't work also
            }
        }
    }

    private Structure(datenStruct struc) 
    {
        this.USE = struc;
    }

    internal bool AddSubject(Sub subject, int day, int period)
    {
        //add the subject into inner lists (room)
        return true;
    }

    public void PrintStruct()
    {
        for (int i = 0; i < USE.days; i++)
        {
            for (int j = 0; j < USE.periods; j++)
            {
                foreach (var subject in USE.schedualArray[i, j].Subjects)
                {
                    Console.Write("\t\t");
                }
                Console.Write("\t\t");
            }
            Console.WriteLine();
        }
    }

    public List<Room> CloneList(ref List<Room> rooms)
    {
        var lst = new List<Room>();
        foreach (var room in rooms)
        {
            lst.Add((Room)room.Clone());
        }
        return lst;
    }

    internal void RemoveSubject(Sub subject)
    {
        //..................
    }

    #region IDisposable Members

    public void Dispose()
    {
       // GC.Collect(g, GCCollectionMode.Forced);
    }

    #endregion

    public object Clone()
    {
        var copie =new datenStruct();

        copie.days = USE.days;
        copie.subjectCount = USE.subjectCount;
        copie.periods = USE.periods;


        var RoomListCopie = new List<Room>();

        foreach (Room origSub in USE.rooms)
            RoomListCopie.Add((Room)origSub.Clone());

        copie.rooms = RoomListCopie;


        copie.schedualArray = new Period[copie.days, copie.periods];
        for (int i = 0; i < copie.days; i++)
        {
            for (int j = 0; j < copie.periods; j++)
            {
                copie.schedualArray[i, j] = new Period(CloneList(ref copie.rooms));
            }
        }

        return new Structure(copie);
    }
}
WiiMaxx
  • 5,322
  • 8
  • 51
  • 89
  • does this method clone the entire list or just the references without the real objects ????? – Yaser Jaradeh Mar 16 '13 at 20:12
  • it worked and it gave me an exception :( System.Windows.Markup.XamlParseException occurred Message='No matching constructor found on type 'Structure.Structure'. You can use the Arguments or FactoryMethod directives to construct this type.' Line number '1' and line position '12'. – Yaser Jaradeh Mar 17 '13 at 11:02
  • can anyone helps me what this means ?? – Yaser Jaradeh Mar 17 '13 at 11:02
  • @YaserJaradeh could you please show me how you implement my code snippet – WiiMaxx Mar 17 '13 at 11:51
  • thank you for your cooperation with me i have just updated the code above to show you the implementation :) – Yaser Jaradeh Mar 17 '13 at 14:25
  • yes i just added your code but it didn't work :( the compiler is giving me Error 1 'Structure.Room' does not contain a definition for 'Clone' and no extension method 'Clone' accepting a first argument of type 'Structure.Room' could be found (are you missing a using directive or an assembly reference?) – Yaser Jaradeh Mar 18 '13 at 18:17
  • no in code above there is such a method look in the **Room.cs** part – WiiMaxx Mar 19 '13 at 07:12
  • yes i know it doesn't make any sense but i swear to you that's what the compiler is giving me ??? – Yaser Jaradeh Mar 19 '13 at 15:53
  • @YaserJaradeh did you modified something? because **no extension method 'Clone' accepting a first argument of type 'Structure.Room'** looks for my like you tried something like `Clone(obj)` where **obj** Type == `Structure.Room` – WiiMaxx Mar 19 '13 at 16:02
  • No, i have double checked the code it's still the same :( it's driving me crazy do you recommend me to try it on another version of visual studio (eg. VS 2008) – Yaser Jaradeh Mar 19 '13 at 16:47
  • It did work when i put it in this format #region ICloneable Members public object Clone() { string stCopie = System.Windows.Markup.XamlWriter.Save(this); var Copie = System.Windows.Markup.XamlReader.Load(System.Xml.XmlReader.Create(new System.IO.StringReader(stCopie))) as Structure; return Copie; } #endregion and it still also gives me the same exception ?????? – Yaser Jaradeh Mar 19 '13 at 16:53
  • @YaserJaradeh ok i'm out off ideas sry – WiiMaxx Mar 19 '13 at 21:26
  • thanks very much for your huge help i appreciate what have you done for me :) – Yaser Jaradeh Mar 20 '13 at 18:06
  • Good day sir, after a lot of work the method you gave me worked after providing a public parametlles constructor but when doing that the clone method work but not all the objects inside are cloned, i will updated my classes above hope you can help me – Yaser Jaradeh Mar 21 '13 at 07:57
  • @YaserJaradeh which objects arn't get cloned? – WiiMaxx Mar 21 '13 at 08:02
  • i updated the code above the main object is `period[,] schedualArray;` – Yaser Jaradeh Mar 21 '13 at 08:04
  • oh now i know way because it's only clones your public values. I doesn't know that before :( sry i will udate my answer – WiiMaxx Mar 21 '13 at 08:38
  • i'm tring this edit it seems to be working but after a few clones it seems to start missing a few fields specially `period[,] schedualArray;` but i'm still working on it thanks very much sir – Yaser Jaradeh Mar 21 '13 at 14:21
  • @YaserJaradeh which fields are missed? maybe you should set breakpoints to monitor on which moment your data's are disappears. – WiiMaxx Mar 21 '13 at 14:32
  • 1
    Thank you very much i traced the error step by step and i found out where it is thanks very much for your help it's working now ;) – Yaser Jaradeh Mar 23 '13 at 10:08
  • @YaserJaradeh glad to here that – WiiMaxx Mar 24 '13 at 20:57
0

i wrote this code i think it solve my problem but if you have any other answers please inform me.

private Structure SpecialClone(Structure s)
        {
            var rooms = Cloner.DeepClone<List<Room>>(s.Rooms);
            var perds = Cloner.DeepClone<Period[,]>(s.SchedualArray);
            var days = s.Days;
            var periods = s.Periods;
            return new Structure(days, periods, rooms, perds, s.SubjectCount);
        }

and i added an overloading to the Structure Constructure to do this:

public Structure(int days, int periods, List<Room> rooms, Period[,] _periods,int subjectCount)
        {
            this.days = days;
            this.periods = periods;
            this.rooms = rooms.DeepClone();
            this.subjectCount = subjectCount;
            this.schedualArray = _periods.Clone() as Period[,];
        }
Yaser Jaradeh
  • 322
  • 1
  • 6
  • 27
  • it is recommended to use [ICloneable()] (http://msdn.microsoft.com/de-de/library/vstudio/system.icloneable.aspx). I will update my answer with your classes – WiiMaxx Mar 18 '13 at 07:28