Member 14351839 Ответов: 2

Как использовать icloneable интерфейс в C#


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!

исходный текст
GitHub - Triptonix/Справка[^]

Студент.в 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();
       }
   }


Что я уже пробовал:

Я пытался клонировать класс, но не знаю, как применить изменения только к клону, чтобы таким образом изменения были сохранены для текущего ученика только в том случае, если пользователь нажмет кнопку ОК

Gerry Schmitz

Те, кто знает, используют MemberwiseClone() для клонирования неглубоких частей ... так как вам не нужно пересматривать свой "пользовательский код", если вы только добавляете новые (не толстые) свойства.

https://docs.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netframework-4.8

2 Ответов

Рейтинг:
12

BillWoodruff

Иногда проще просто сделать это самому; посмотрите, есть ли у вас какие-то идеи из этого:

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 ?
        }
    }
}
Пример использования:
Student s1 =  new Student("S1", 89,92,100,78);

Student s2 = s1.Clone();

s2.Scores[2] = 87;

s1.SaveEdits(s2);


BillWoodruff

Я бы,вероятно, использовал словарь<datetime, int>, Чтобы отслеживать результаты, чтобы облегчить будущее редактирование и т. д.

Member 14351839

Неужели это успешно клонирует имя и список?

студент публичного класса : ICloneable
{
общественная список<инт&ГТ; баллы = новый список<инт&ГТ;();

публичное строковое имя
{ get; set; }

клон публичного объекта()
{
Студент obj2 = новый студент();
obj2.Scores = это.Рейтинг;
obj2.Название = этот.Название;
вернуться obj2;
}

BillWoodruff

Важно, чтобы вы построили эксперимент с кодом, чтобы проверить это: затем один шаг через него, исследуя значения в каждой строке кода. Затем, если это не сработает, выясните, почему.

Вам нужно изучить разницу между объектами, передаваемыми в виде значений, таких как целые числа, то, как специальный строковый объект является неизменяемым, и то, как другие объекты, такие как списки, передаются по ссылке. Поймите и поймите, как их использовать: вы будете на пути к тому, чтобы стать компетентным программистом C#.

Рейтинг:
0

Christian Graus

На типы значений все равно не ссылаются (int, bool). строки неизменяемы, на них также нет ссылок. Если у вас есть класс, который вы хотите глубоко скопировать, и он содержит экземпляры класса, вам нужно создать новые экземпляры этого класса, иначе на них будут ссылаться


Member 14351839

Но Clone () - это строковый метод, используемый для клонирования объекта string? Итак, вы говорите, что в моем случае, поскольку мои классы содержат экземпляры, мне нужно создавать новые экземпляры? Наверное, я не понимаю, почему я могу клонировать экземпляры.

Christian Graus

Если вы хотите клонировать свой класс, вам нужно просто построить новый класс в интерфейсе, используя значения в вашем объекте. Метод клонирования класса string имеет это в документах:

Возвращаемое значение не является независимой копией этого экземпляра; это просто другое представление тех же данных. Используйте метод Copy или CopyTo для создания отдельного строкового объекта с тем же значением, что и этот экземпляр.

Поскольку метод Clone просто возвращает существующий экземпляр string, нет особых причин вызывать его напрямую.

Строки неизменны. При изменении строки создается новая строка. Это означает, что вы делаете копию, в сущности. Странно, это означает строку a = b более четко создает новую копию строки А = B.Клон()

Строки-это особый случай в C#. Я бы назвал метод клонирования, потому что он существует и делает ваше намерение ясным.