如何在C#中使用icloneable接口 [英] How to use the icloneable interface in C#

查看:107
本文介绍了如何在C#中使用icloneable接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嘿,我正在尝试克隆一个对象。 更新学生分数表单应创建当前学生对象的克隆,然后将更改应用于克隆。这样,只有当用户单击确定按钮时,更改才会保存到当前学生。要创建克隆,Student类需要实现ICloneable接口,而Clone方法需要实现深层复制。到目前为止,我已经了解到,当您需要原始包含的其他对象的副本(而不是指向原始对象的位置的指针)时,您需要深层复制某些内容,以便在进行更改时这些复制的属性,不会影响原始对象。我正在努力的是把它应用到我的模型中。下面是我正在尝试克隆的学生班级和更新学生表格。如果有帮助,我还附上了源代码。谢谢!



SourceCode

GitHub - Triptonix /帮助 [ ^ ]



Student.cs

 public class Student 
{
public List< int>分数=新列表< int>();

公共字符串名称
{get;组; }

public bool AddScore(int score)
{
try
{
Scores.Add(score);
}
catch {return false; }
返回true;
}

public List< int> GetScores()
{
返回分数;
}

public int GetScoreAt(int index)
{
return(int)得分[index];
}

public int GetScoreTotal()
{
int sum = 0;
foreach(得分中的int得分)
{
sum + =得分;
}
返还金额;
}

public int GetScoreCount()
{
return Scores.Count;
}

public int GetScoreAverage()
{
return GetScoreTotal()/ GetScoreCount();
}

public void DestroyScores()
{
Scores = new List< int>();
}
}



frmUpdateScores.cs

公开partial class frmUpdateStudent:Form 
{
private Form1 parentForm; //主要形式
私人学生studentToEdit; //学生列表
private int index; // index

public frmUpdateStudent(Form1 parentForm,int index)//用新学生更新父表单(Form1)并得分
{
this.parentForm = parentForm;
this.index = index;
studentToEdit = this.parentForm.GetStudent(index);

InitializeComponent();

StudentName.Text = studentToEdit.Name;
UpdateScoreDisplay();
}

public void AddScoreToStudent(int value)//为当前学生添加分数并显示在列表
{
studentToEdit.AddScore(value);
UpdateScoreDisplay();
}

public void UpdateScoreAtIndex(int id,int value)//更新从列表中选择的分数
{
studentToEdit.GetScores()[id] =值;
UpdateScoreDisplay();
}

public int GetScoreAtIndex(int id)//得分指数
{
return studentToEdit.GetScoreAt(id);
}

private void UpdateScoreDisplay()//更新分数显示列表
{
CurrentScores.DataSource = null;
CurrentScores.DataSource = studentToEdit.GetScores();
}

private void AddScoreButton_Click(object sender,EventArgs e)//打开添加分数表格
{
frmAddScore addScoreForm = new frmAddScore(this);
addScoreForm.Show();
}

private void RemoveScoreButton_Click_1(object sender,EventArgs e)//从当前索引中删除分数并更新显示列表
{
studentToEdit.GetScores()。 RemoveAt移除(CurrentScores.SelectedIndex);
UpdateScoreDisplay();
}

private void ClearScoresButton_Click_1(object sender,EventArgs e)//清除所有分数
{
studentToEdit.DestroyScores();
UpdateScoreDisplay();
}

private void CloseButton_Click_1(object sender,EventArgs e)
{
Close(); //关闭表单
}

private void UpdateButton_Click_1(对象发件人,EventArgs e)//为当前学生打开更新表单
{
学生表1 =新学生( );
Form1.Name = StudentName.Text;
parentForm.UpdateStudent(index,Form1);
关闭();
}

private void UpdateScoresButton_Click(object sender,EventArgs e)
{
frmUpdateScore updateScoreForm = new frmUpdateScore(this,CurrentScores.SelectedIndex);
updateScoreForm.Show();
}
}





我尝试了什么:



我试图克隆该类,但不确定如何仅将更改应用于克隆,这样,更改将仅保存到当前学生如果用户单击OK按钮

解决方案

无论如何都不引用值类型(int,bool)。字符串是不可变的,它们也没有被引用。如果你有一个想要深层复制的类,并且它包含类实例,你需要构建该类的新实例,否则它们将被引用


有时它更容易实现它自己;看看你是否从中获得了一些想法:

使用System; 
使用System.Collections.Generic;

namespace YourNameSpace
{
public class Student
{
public List< int>成绩;

public Student CloneOf;

public Student(string name,params int []得分)
{
CloneOf = null;

姓名=姓名;
分数=新名单< int>(分数);
}

public string Name {get;组; }

public bool AddScore(int score)
{
try
{
Scores.Add(score);
}
catch {return false; }
返回true;
}

public List< int> GetScores()
{
返回分数;
}

public int GetScoreAt(int index)
{
return(int)得分[index];
}

public int GetScoreTotal()
{
int sum = 0;
foreach(得分中的int得分)
{
sum + =得分;
}
返还金额;
}

public int GetScoreCount()
{
return Scores.Count;
}

public int GetScoreAverage()
{
return GetScoreTotal()/ GetScoreCount();
}

public void DestroyScores()
{
Scores = new List< int>();
}
}

public static class StudentExtensions
{
public static Student Clone(this Student stdnt)
{
var clone = new学生

stdnt.Name,
stdnt.Scores.ToArray()

//注意在这里使用对象初始化程序
{
CloneOf = stdnt
};

返回克隆;
}

public static void SaveEdits(this Student stdnt,Student clone)
{
if(clone == null || clone.CloneOf == null || clone.CloneOf!= stdnt)
{
抛出新的ArgumentException(无效的克隆参数);
}


if(stdnt.Scores!= clone.Scores)stdnt.Scores = clone.Scores;
if(stdnt.Name!= clone.Name)stdnt.Name = clone.Name;

//考虑在这里处理克隆?
}
}
}

用法示例:

学生s1 =新学生(S1,89,92,100,78); 

学生s2 = s1.Clone();

s2.Scores [2] = 87;

s1.SaveEdits(s2);


Hey, I'm attempting to clone an object. The Update Student Scores form should create a clone of the current student object and then apply changes to the clone. That way the changes will be saved to the current student only if the user clicks the OK button. To create a clone, the Student class will need to implement the ICloneable interface, and the Clone method will need to implement a deep copy. So far, I've learned that you want a deep copy of something when you need a copy of the other objects contained by the original (rather than pointers to the location of those objects in the original), so that when you make changes to these copied properties, you don't affect the original object. What I'm struggling with though is applying it to my model. Below is the student class I'm trying to clone and the update student form. I've also attached the source code if that helps. Thanks!

SourceCode
GitHub - Triptonix/Help[^]

Student.cs

public class Student
    {
        public List<int> Scores = new List<int>();

        public string Name
        { get; set; }

        public bool AddScore(int score)
        {
            try
            {
                Scores.Add(score);
            }
            catch { return false; }
            return true;
        }

        public List<int> GetScores()
        {
            return Scores;
        }

        public int GetScoreAt(int index)
        {
            return (int)Scores[index];
        }

        public int GetScoreTotal()
        {
            int sum = 0;
            foreach (int score in Scores)
            {
                sum += score;
            }
            return sum;
        }

        public int GetScoreCount()
        {
            return Scores.Count;
        }

        public int GetScoreAverage()
        {
            return GetScoreTotal() / GetScoreCount();
        }

        public void DestroyScores()
        {
            Scores = new List<int>();
        }
    }


frmUpdateScores.cs

public partial class frmUpdateStudent : Form
   {
       private Form1 parentForm;  //main form
       private Student studentToEdit; //student list
       private int index; //index

       public frmUpdateStudent(Form1 parentForm, int index)  //update parent form (Form1) with the new student and scores
       {
           this.parentForm = parentForm;
           this.index = index;
           studentToEdit = this.parentForm.GetStudent(index);

           InitializeComponent();

           StudentName.Text = studentToEdit.Name;
           UpdateScoreDisplay();
       }

       public void AddScoreToStudent(int value) //add score to current student and display in the list
       {
           studentToEdit.AddScore(value);
           UpdateScoreDisplay();
       }

       public void UpdateScoreAtIndex(int id, int value)  //update a score selected from the list
       {
           studentToEdit.GetScores()[id] = value;
           UpdateScoreDisplay();
       }

       public int GetScoreAtIndex(int id)  //get the score index
       {
           return studentToEdit.GetScoreAt(id);
       }

       private void UpdateScoreDisplay()  //update the score display list
       {
           CurrentScores.DataSource = null;
           CurrentScores.DataSource = studentToEdit.GetScores();
       }

       private void AddScoreButton_Click(object sender, EventArgs e)  //open the add score form
       {
           frmAddScore addScoreForm = new frmAddScore(this);
           addScoreForm.Show();
       }

       private void RemoveScoreButton_Click_1(object sender, EventArgs e) //remove a score from current index and update display list
       {
           studentToEdit.GetScores().RemoveAt(CurrentScores.SelectedIndex);
           UpdateScoreDisplay();
       }

       private void ClearScoresButton_Click_1(object sender, EventArgs e) //clear all scores
       {
           studentToEdit.DestroyScores();
           UpdateScoreDisplay();
       }

       private void CloseButton_Click_1(object sender, EventArgs e)
       {
           Close();  //close form
       }

       private void UpdateButton_Click_1(object sender, EventArgs e)  //open update form for current student
       {
           Student Form1 = new Student();
           Form1.Name = StudentName.Text;
           parentForm.UpdateStudent(index, Form1);
           Close();
       }

       private void UpdateScoresButton_Click(object sender, EventArgs e)
       {
           frmUpdateScore updateScoreForm = new frmUpdateScore(this, CurrentScores.SelectedIndex);
           updateScoreForm.Show();
       }
   }



What I have tried:

I've tried to clone the class, but not sure how to apply the changes only to the clone so that way, the changes will be saved to the current student only if the user clicks the OK button

解决方案

Value types are not referenced anyhow (int, bool). strings are immutable, they are also not referenced. If you have a class you want to deep copy, and it contains class instances, you need to build new instances of that class, or they will be referenced


Sometimes it's easier to just do it yourself; see if you get some ideas from this:

using System;
using System.Collections.Generic;

namespace YourNameSpace
{
    public class Student
    {
        public List<int> Scores;

        public Student CloneOf;

        public Student(string name, params int[] scores)
        {
            CloneOf = null;

            Name = name;
            Scores = new List<int>(scores);
        }

        public string Name { get; set; }

        public bool AddScore(int score)
        {
            try
            {
                Scores.Add(score);
            }
            catch { return false; }
            return true;
        }

        public List<int> GetScores()
        {
            return Scores;
        }

        public int GetScoreAt(int index)
        {
            return (int)Scores[index];
        }

        public int GetScoreTotal()
        {
            int sum = 0;
            foreach (int score in Scores)
            {
                sum += score;
            }
            return sum;
        }

        public int GetScoreCount()
        {
            return Scores.Count;
        }

        public int GetScoreAverage()
        {
            return GetScoreTotal() / GetScoreCount();
        }

        public void DestroyScores()
        {
            Scores = new List<int>();
        }
    }

    public static class StudentExtensions
    {
        public static Student Clone(this Student stdnt)
        {
            var clone = new Student
            (
                stdnt.Name,
                stdnt.Scores.ToArray()
            )
            // note use of object initializer here
            {
                CloneOf = stdnt
            };

            return clone;
        }
        
        public static void SaveEdits(this Student stdnt, Student clone)
        {
            if (clone == null || clone.CloneOf == null || clone.CloneOf != stdnt)
            {
                throw new ArgumentException("Invalid clone argument");
            }

            if (stdnt.Scores != clone.Scores) stdnt.Scores = clone.Scores;
            if (stdnt.Name != clone.Name) stdnt.Name = clone.Name;

            // consider disposing of clone here ?
        }
    }
}

Usage example:

Student s1 =  new Student("S1", 89,92,100,78);

Student s2 = s1.Clone();

s2.Scores[2] = 87;

s1.SaveEdits(s2);


这篇关于如何在C#中使用icloneable接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆