- Автор темы
- #1
Практический пример. Поверхностное и глубокое копирование
В этом разделе мы рассмотрим проблему копирования объектов ссылочных типов. Дело в том, что при копировании структурных типов копируются сами объекты, а при копировании ссылочных - ссылки (в терминах C++ указатели). Проблему частично помогает решить защищенный метод MemberwiseClone() класса Object, который неявно наследуют все классы.
Данный метод создает объект-копию данного, которую можно затем использовать в собственных целях. Казалось бы, чего еще? Однако метод MemberwiseClone() осуществляет лишь поверхностное копирование, что означает копирование объектов для структурных типов и копирование ссылок на объекты для ссылочных типов. Возникает проблема, которую вы уже изучали в курсе C++: отсутствие конструктора копирование и перегруженного оператора присваивания для классов, имеющих указатели в качестве полей класса.
Решение заключается в создании функции, которая будет осуществлять "глубокое" копирование (увы, в языке C# оператор присваивания не перегружается). Чаще всего для этих целей используется метод Clone(), описанный в интерфейсе IClonable. Реализация данной функции, естественно, ваше личное дело :).
Более подробную информацию можно получить, изучив практические
примеры, или заглянув в MSDN.
Домашнее задание
Разработать иерархию классов для рисования простых геометрических фигур (звездочками). необходимо обеспечить возможность отрисовки прямоугольников, треугольников, ромбов (все диагональные линии идут под углом 45 градусов). Оценивается качество и реализация модели.
В этом разделе мы рассмотрим проблему копирования объектов ссылочных типов. Дело в том, что при копировании структурных типов копируются сами объекты, а при копировании ссылочных - ссылки (в терминах C++ указатели). Проблему частично помогает решить защищенный метод MemberwiseClone() класса Object, который неявно наследуют все классы.
Данный метод создает объект-копию данного, которую можно затем использовать в собственных целях. Казалось бы, чего еще? Однако метод MemberwiseClone() осуществляет лишь поверхностное копирование, что означает копирование объектов для структурных типов и копирование ссылок на объекты для ссылочных типов. Возникает проблема, которую вы уже изучали в курсе C++: отсутствие конструктора копирование и перегруженного оператора присваивания для классов, имеющих указатели в качестве полей класса.
Решение заключается в создании функции, которая будет осуществлять "глубокое" копирование (увы, в языке C# оператор присваивания не перегружается). Чаще всего для этих целей используется метод Clone(), описанный в интерфейсе IClonable. Реализация данной функции, естественно, ваше личное дело :).
Код:
using System;
namespace CSharpApplication.CloneExample
{
// Класс студент
class Student
{
// Имя
string FirstName;
// Фамилия
string LastName;
// Адрес
string Address;
// Телефон
string Phone;
// Дата рождения
DateTime BirthDay;
// Поверхностное копирование объекта
public Student Clone()
{
// Вызываем функцию базового класса (Object)
// для поверхностного копирования объекта
return (Student)MemberwiseClone();
}
// Ввод данных
public void Input()
{
Console.WriteLine("*****Ввод данных о студенте:******");
Console.Write("Имя: ");
FirstName = Console.ReadLine();
Console.Write("Фамилия: ");
LastName = Console.ReadLine();
Console.Write("Адрес: ");
Address = Console.ReadLine();
Console.Write("Телефон: ");
Phone = Console.ReadLine();
Console.Write("Дата рождения: ");
try
{
BirthDay = Convert.ToDateTime(Console.ReadLine());
}
catch
{
Console.WriteLine("Ошибка ввода, используем текущую дату");
BirthDay = DateTime.Now;
}
Console.WriteLine("**********************************");
}
// Вывод данных
public void Print()
{
Console.WriteLine("*****Вывод данных о студенте:*****");
Console.WriteLine("Имя: {0}", FirstName);
Console.WriteLine("Фамилия: {0}", LastName);
Console.WriteLine("Адрес: {0}", Address);
Console.WriteLine("Телефон: {0}", Phone);
Console.WriteLine("Дата рождения: {0}.{1}.{2}",
BirthDay.Day, BirthDay.Month, BirthDay.Year);
Console.WriteLine("**********************************");
}
}
// Класс Group
class Group : ICloneable
{
// Название группы
string GroupName;
// Массив студентов
Student [] st;
// Конструктор, получающий название группы и количество студентов
public Group(string gn, int n)
{
GroupName = gn;
// По умолчанию в группе 10 студентов
if(n <= 0 || n > 10)
n = 10;
st = new Student[n];
// Создаем студентов
for(int i = 0; i < n; i++)
st[i] = new Student();
}
// Аналог конструктора копирования
public Group(Group gr)
{
// Создаем массив студентов
st = new Student[gr.st.Length];
// Передираем название группы
GroupName = gr.GroupName;
// Передираем каждого индивидуума
for(int i = 0; i < gr.st.Length; i++)
st[i] = gr.st[i].Clone();
}
// Заполняем группу
public void Input()
{
for(int i = 0; i < st.Length; i++)
{
Console.WriteLine("{0}.", i + 1);
st[i].Input();
}
}
// Изменение данных конкретного студента
public void InputAt(int n)
{
if(st == null || n >= st.Length || n < 0)
return;
st[n].Input();
}
// Вывод списка группы
public void Print()
{
Console.WriteLine("Группа {0}:", GroupName);
for(int i = 0; i < st.Length; i++)
{
Console.WriteLine("{0}.", i + 1);
st[i].Print();
}
}
// Вывод информации о конкретном студенте
public void PrintAt(int n)
{
if(st == null || n >= st.Length || n < 0)
return;
st[n].Print();
}
// Получение имени группы
public string GetGroupName()
{
return GroupName;
}
// Изменение имени группы
public void SetGroupName(string gn)
{
GroupName = gn;
}
// Поверхностное копирование
public new object MemberwiseClone()
{
// Функция класса Object
return base.MemberwiseClone();
}
// "Глубокое" копирование,
// реализация функции из интерфейса IClonable
public object Clone()
{
// Создание новой группы
Group gr = new Group(GroupName, st.Length);
// Передираем каждого индивидуума
for(int i = 0; i < st.Length; i++)
gr.st[i] = st[i].Clone();
// Возврат независимой копии группы
return gr;
}
}
// Тестирование
class Test
{
static void Main()
{
Group grcopy;
// Группа из одного студента (для простоты ввода)
Group gr = new Group("АС-951", 1);
gr.Input();
gr.Print();
/************************************************************/
/* Копирование ссылок приводит к созданию двух ссылок
/* на один и тот же объект
/************************************************************/
grcopy = gr;
grcopy.SetGroupName("АМ-951");
// Изменяем данные
grcopy.InputAt(0);
// Вывод (информация окажется одинаковой)
Console.WriteLine(grcopy.GetGroupName());
grcopy.PrintAt(0);
Console.WriteLine(gr.GetGroupName());
gr.PrintAt(0);
/************************************************************/
/* Поверхностное копирование, название групп будут
/* различны, но ссылки st у обоих объектов будут
/* хранить одинаковые адреса
/************************************************************/
grcopy = (Group)gr.MemberwiseClone();
grcopy.SetGroupName("АП-951");
// Изменяем данные
grcopy.InputAt(0);
// Вывод (информация о студенте окажется одинаковой)
Console.WriteLine(grcopy.GetGroupName());
grcopy.PrintAt(0);
Console.WriteLine(gr.GetGroupName());
gr.PrintAt(0);
/************************************************************/
/* "Глубокое" копирование, мы получаем два независимых объекта
/************************************************************/
grcopy = (Group)gr.Clone();
grcopy.SetGroupName("АТ-951");
// Изменяем данные
grcopy.InputAt(0);
// Вывод
Console.WriteLine(grcopy.GetGroupName());
grcopy.PrintAt(0);
Console.WriteLine(gr.GetGroupName());
gr.PrintAt(0);
/************************************************************/
/* Аналог конструктора копирования
/************************************************************/
Group gg = new Group(grcopy);
grcopy.SetGroupName("АИ-951");
// Изменяем данные
grcopy.InputAt(0);
// Вывод
Console.WriteLine(grcopy.GetGroupName());
grcopy.PrintAt(0);
Console.WriteLine(gg.GetGroupName());
gg.PrintAt(0);
}
}
}
Более подробную информацию можно получить, изучив практические
примеры, или заглянув в MSDN.
Домашнее задание
Разработать иерархию классов для рисования простых геометрических фигур (звездочками). необходимо обеспечить возможность отрисовки прямоугольников, треугольников, ромбов (все диагональные линии идут под углом 45 градусов). Оценивается качество и реализация модели.