Главная страница
Библиотека (скачать книги)
Скачать софт
Введение в программирование
Стандарты для C++
Уроки по C#
Уроки по Python
HTML
Веб-дизайн
Ассемблер в среде Windows
ActiveX
Javascript
Общее о Линукс
Линукс - подробно
Линукс - новое
Delphi
Паскаль для начинающих
Турбопаскаль
Новости
Партнеры
Наши предложения
Архив новостей





Практический пример. Поверхностное и глубокое копирование

В этом разделе мы рассмотрим проблему копирования объектов ссылочных типов. Дело в том, что при копировании структурных типов копируются сами объекты, а при копировании ссылочных - ссылки (в терминах 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 градусов). Оценивается качество и реализация модели.


 
 
 

Библиотека программиста. 2009.
Администратор: admin@programmer-lib.ru