【问题标题】:Cloning An Object which contains many objects and lists C#克隆一个包含许多对象和列表的对象 C#
【发布时间】:2013-03-16 18:55:16
【问题描述】:

您好 :) 我在克隆包含 winForm 中的其他对象和列表的对象时遇到问题。 我想克隆一个结构类型的对象下面提供了类 首先我尝试过浅克隆

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

它不起作用,所以我搜索了https://stackoverflow.com/,然后我做了一个深度克隆

Deep cloning objects 还有这个 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);
 }
}

我遇到的问题是我正在获取对象的备份副本

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

然后更改this.sched备份更改:(

我的课程结构是:

[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
}

我现在真的不知道克隆过程的细节是什么,但我的要求是克隆整个对象以及对象的结构和层次结构中的连续对象以及列表指向不仅有参考 请谁能帮帮我,我真的迷路了:(,提前感谢所有有兴趣帮助我的人:) Yaser

EDIT使用@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;
}

但内部period[,] 被克隆为null 你能帮我吗@WiiMaxx

编辑 2.0 我还检查了这篇关于 3 种方法的深度克隆的文章,我认为我的代码中有循环引用,这就是为什么这对我不起作用 http://blog.nuclex-games.com/mono-dotnet/fast-deep-cloning/ 结束编辑

【问题讨论】:

  • 拥有这样的代码是很好的。现在,请描述克隆操作的精确要求,逐步进入对象层次结构。
  • @PieterGeerkens 我刚刚更新了我的问题的页脚,希望能涵盖你的想法:)
  • 我不相信你 :) 你能否添加一个显示意外行为的小程序,即创建一个对象,克隆它,修改原始对象并查看克隆更改(例如,使用 Console. Writeline 从两个对象中写出一些状态)。另外:你不应该在 Dispose 中强行 GC 东西。它并没有做任何真正有用的事情。
  • 我一直在网上阅读,我想出了一个范例(循环引用)我的代码有这个吗????谁能帮帮我??????

标签: c# reference clone circular-reference


【解决方案1】:

好的,现在它应该真的可以工作了
(我也删除了我的旧解决方案,因为它完全不符合您的需求)

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

结构.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);
    }
}

【讨论】:

  • 这个方法是克隆整个列表还是只克隆没有真实对象的引用?????
  • 它起作用了,它给了我一个异常:( System.Windows.Markup.XamlParseException 发生 Message='No matching constructor found on type 'Structure.Structure'。您可以使用 Arguments 或 FactoryMethod 指令来构造这个类型。'行号'1'和行位置'12'。
  • 谁能帮我看看这是什么意思??
  • @YaserJaradeh 你能告诉我你是如何实现我的代码 sn-p
  • 非常感谢,我一步一步追踪错误,我发现它在哪里非常感谢您的帮助,它现在正在工作;)
【解决方案2】:

我写了这段代码,我认为它解决了我的问题,但如果您有任何其他答案,请告诉我。

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

我在结构构造中添加了一个重载来做到这一点:

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[,];
        }

【讨论】:

猜你喜欢
  • 2019-02-26
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
  • 1970-01-01
  • 2015-04-25
  • 1970-01-01
  • 1970-01-01
  • 2014-07-16
相关资源
最近更新 更多