Программирование

Автор работы: Пользователь скрыл имя, 28 Марта 2012 в 11:49, курс лекций

Описание

Лекции по дисциплине "Программирование"

Содержание

Лекция 1. Состав языка Типы данных Переменные и операции

Лекция 2. Линейные программы

Лекция 3. Простейшие операторы. Операторы ветвления

Лекция 4. Операторы цикла и передачи управления

Лекция 5. Обработка исключительных ситуаций

Лекция 6. Классы: основные понятия Описание класса

Лекция 7. Параметры методов

Лекция 8. Конструкторы и свойства

Лекция 9. . Массивы

Лекция 10. Символы и строки

Лекция 11 Дополнительные возможности методов. Индексаторы

Лекция 12. Операции класса. Деструкторы

Лекция 13. Наследование классов

Лекция 14. Интерфейсы

Лекция 15. Стандартные интерфейсы .NET

Лекция 16. Структуры и перечисления

Лекция 17. Делегаты

Лекция 18. События

Работа состоит из  1 файл

Лекции C#.doc

— 847.00 Кб (Скачать документ)

                               name, health, ammo );

        }

 

        public class SortByName : IComparer                               //

        {

            int IComparer.Compare( object ob1, object ob2 )

            {

                Monster m1 = (Monster) ob1;

                Monster m2 = (Monster) ob2;

                return String.Compare( m1.Name, m2.Name );

            }

        }

 

        public class SortByAmmo : IComparer                               //

        {

            int IComparer.Compare( object ob1, object ob2 )

            {

                Monster m1 = (Monster) ob1;

                Monster m2 = (Monster) ob2;

                if ( m1.Ammo > m2.Ammo ) return  1;

                if ( m1.Ammo < m2.Ammo ) return -1;

                return 0;

            }

        }

        string name;

        int health, ammo;

    }

   

    class Class1

    {   static void Main()

        {

            const int n = 3;

            Monster[] stado = new Monster[n];

 

            stado[0] = new Monster( 50, 50, "Вася" );

            stado[1] = new Monster( 80, 80, "Петя" );

            stado[2] = new Monster( 40, 10, "Маша" );

 

            Console.WriteLine( "Сортировка по имени:" );

            Array.Sort( stado, new Monster.SortByName() );

            foreach ( Monster elem in stado ) elem.Passport();

 

            Console.WriteLine( "Сортировка по вооружению:" );

            Array.Sort( stado, new Monster.SortByAmmo() );

            foreach ( Monster elem in stado ) elem.Passport();

        }

    }

}

 

Результат работы программы:

Сортировка по имени:

Monster Вася     health = 50 ammo = 50

Monster Маша     health = 40 ammo = 10

Monster Петя     health = 80 ammo = 80

Сортировка по вооружению:

Monster Маша     health = 40 ammo = 10

Monster Вася     health = 50 ammo = 50

Monster Петя     health = 80 ammo = 80

 

Перегрузка операций отношения

Если класс реализует интерфейс IComparable, его экземпляры можно сравнивать между собой на больше-меньше. Логично разрешить использовать для этого операции отношения, перегрузив их. Операции должны перегружаться парами: < и >, <= и >=, == и !=. Перегрузка операций обычно выполняется путем делегирования, то есть обращения к переопределенным методам CompareTo и Equals.

Листинг 17.3. Перегрузка операций отношения

using System;

namespace ConsoleApplication1

{

    class Monster : IComparable

    {

        public Monster( int health, int ammo, string name )

        {

            this.health = health;

            this.ammo   = ammo;

            this.name   = name;

        }

         public override bool Equals( object obj )

        {

            if ( obj == null || GetType() != obj.GetType() ) return false;

             Monster temp = (Monster) obj;

            return health == temp.health &&

                   ammo   == temp.ammo   &&

                   name   == temp.name;

        }

         public override int GetHashCode()

        {

            return name.GetHashCode();

        }

         public static bool operator == ( Monster a, Monster b )

        {

            return a.Equals( b );

        }

 

        public static bool operator != ( Monster a, Monster b )

        {

            return ! a.Equals( b );

        }

         public static bool operator < ( Monster a, Monster b )

        {

            return ( a.CompareTo( b ) < 0 );

        }

               public static bool operator > ( Monster a, Monster b )

        {

            return ( a.CompareTo( b ) > 0 );

        }

       

        public static bool operator <= ( Monster a, Monster b )

        {

            return ( a.CompareTo( b ) <= 0 );

        }

               public static bool operator >= ( Monster a, Monster b )

        {

            return ( a.CompareTo( b ) >= 0 );

        }

 

        public int CompareTo( object obj )

        {

            Monster temp = (Monster) obj;

            if ( this.health > temp.health ) return  1;

            if ( this.health < temp.health ) return -1;

            return 0;

        }

         string name;

        int health, ammo;

    }

     class Class1

    {   static void Main()

        {

            Monster Вася = new Monster( 70, 80, "Вася" );

            Monster Петя = new Monster( 80, 80, "Петя" );

       

            if      ( Вася > Петя )  Console.WriteLine( "Вася больше Пети");

            else if ( Вася == Петя ) Console.WriteLine( "Вася == Петя");

            else                     Console.WriteLine( "Вася меньше Пети");

        }

    }

}

  Результат работы программы:

Вася меньше Пети

 

Клонирование объектов

Клонирование — это создание копии объекта. Копия объекта называется клоном. Как известно, при присваивании одного объекта ссылочного типа другому копируется ссылка, а не сам объект. Если необходимо скопировать в другую область памяти поля объекта, можно воспользоваться методом MemberwiseClone, который любой объект наследует от класса object. При этом объекты, на которые указывают поля объекта, в свою очередь являющиеся ссылками, не копируются. Это называется поверхностным клонированием.

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

Объект, имеющий собственные алгоритмы клонирования, должен объявляться как наследник интерфейса ICloneable и переопределять его единственный метод Clone. В листинге 17.4 приведен пример создания поверхностной копии объекта класса Monster с помощью метода MemberwiseClone, а также реализован интерфейс ICloneable. В демонстрационных целях в имя клона объекта добавлено слово «Клон».

Листинг 17.4. Клонирование объектов

using System;

namespace ConsoleApplication1

{

    class Monster : ICloneable

    {

        public Monster( int health, int ammo, string name )

        {

            this.health = health;

            this.ammo   = ammo;

            this.name   = name;

        }

        public Monster ShallowClone()                 // поверхностная копия

        {

            return (Monster)this.MemberwiseClone();

        }

 

        public object Clone()                      // пользовательская копия

        {

            return new Monster(this.health, this.ammo, "Клон " + this.name);

        }

 

        virtual public void Passport()

        {

            Console.WriteLine( "Monster {0} \t health = {1} ammo = {2}",

                               name, health, ammo );

        }

 

        string name;

        int health, ammo;

    }

       

    class Class1

    {   static void Main()

        {

            Monster Вася = new Monster( 70, 80, "Вася" );

            Monster X = Вася;

            Monster Y = Вася.ShallowClone();

            Monster Z = (Monster)Вася.Clone();

            ...

        }

    }

}

 

Объект Х ссылается на ту же область памяти, что и объект Вася. Следовательно, если мы внесем изменения в один из этих объектов, это отразится на другом. Объекты Y и Z, созданные путем клонирования, обладают собственными копиями значений полей и независимы от исходного объекта.

Перебор объектов (интерфейс IEnumerable) и итераторы

Оператор foreach является удобным средством перебора элементов объекта. Массивы и все стандартные коллекции библиотеки .NET позволяют выполнять такой перебор благодаря тому, что в них реализованы интерфейсы IEnumerable и IEnumerator. Для применения оператора foreach к пользовательскому типу данных требуется реализовать в нем эти интерфейсы.

Интерфейс IEnumerable (перечислимый) определяет всего один метод —GetEnumerator, возвращающий объект типа IEnumerator (перечислитель), который можно использовать для просмотра элементов объекта.

Интерфейс IEnumerator задает три элемента:

                    свойство Current, возвращающее текущий элемент объекта;

                    метод MoveNext, продвигающий перечислитель на следующий элемент объекта;

                    метод Reset, устанавливающий перечислитель в начало просмотра.

Цикл foreach использует эти методы для перебора элементов, из которых состоит объект.

Таким образом, если требуется, чтобы для перебора элементов класса мог применяться цикл foreach, необходимо реализовать четыре метода: GetEnumerator, Current, MoveNext и Reset. Это не интересная работа, а выполнять ее приходится часто, поэтому в версию 2.0 были введены средства, облегчающие выполнение перебора в объекте — итераторы.

Итератор представляет собой блок кода, задающий последовательность перебора элементов объекта. На каждом проходе цикла foreach выполняется один шаг итератора, заканчивающийся выдачей очередного значения. Выдача значения выполняется с помощью ключевого слова yield.

Рассмотрим создание итератора на примере (листинг 17.5). Пусть требуется создать объект, содержащий боевую группу экземпляров типа Monster.

Листинг 17.5. Класс с итератором

using System;

using System.Collections;

namespace ConsoleApplication1

{

    class Monster { ... }

    class Daemon  { ... }

    class Stado : IEnumerable                                           // 1

    {

        private Monster[] mas;

        private int n;

 

        public Stado()

        {

            mas = new Monster[10];

            n = 0;

        }

 

        public IEnumerator GetEnumerator()

        {

            for ( int i = 0; i < n; ++i ) yield return mas[i];          // 2

        }

 

        public void Add( Monster m )

        {

            if ( n >= 10 ) return;

            mas[n] = m;

            ++n;

        }

    }

 

    class Class1

    {   static void Main()

        {

            Stado s = new Stado();

            s.Add( new Monster() );

            s.Add( new Monster("Вася") );

            s.Add( new Daemon() );

           

            foreach ( Monster m in s ) m.Passport();

        }

    }

}

 

Все, что требуется сделать в версии 2.0 для поддержки перебора — указать, что класс реализует интерфейс IEnumerable (оператор 1), и описать итератор (оператор 2). Доступ к нему может быть осуществлен через методы MoveNext и Current интерфейса IEnumerator.

Преимущество использования итераторов заключается в том, что для одного и того же класса можно задать различный порядок перебора элементов. В листинге 17.6 описаны две дополнительные стратегии перебора элементов класса Stado, введенного в листинге 17.5 — перебор в обратном порядке и выборка только тех объектов, которые являются экземплярами класса Monster.

Листинг 17.6. Реализация нескольких стратегий перебора

using System;

using System.Collections;

using MonsterLib;

 

namespace ConsoleApplication1

{

    class Monster { ... }

    class Daemon  { ... }

    class Stado : IEnumerable

    {

        private Monster[] mas;

        private int n;

        public Stado()

        {

            mas = new Monster[10];

            n = 0;

        }

        public IEnumerator GetEnumerator()

        {

            for ( int i = 0; i < n; ++i ) yield return mas[i];

        }

        public IEnumerable Backwards()                 // в обратном порядке

        {

            for ( int i = n - 1; i >= 0; --i ) yield return mas[i];

        }

 

        public IEnumerable MonstersOnly()                  // только монстры

        {

            for ( int i = 0; i < n; ++i )

                if ( mas[i].GetType().Name == "Monster" )

                    yield return mas[i];

        }

        public void Add( Monster m )

        {

            if ( n >= 10 ) return;

            mas[n] = m;

            ++n;

        }

    }

 

    class Class1

    {   static void Main()

        {

            Stado s = new Stado();

            s.Add( new Monster() );

            s.Add( new Monster("Вася") );

            s.Add( new Daemon() );

           

            foreach ( Monster i in s )                i.Passport();

            foreach ( Monster i in s.Backwards() )    i.Passport();

            foreach ( Monster i in s.MonstersOnly() ) i.Passport();

        }

    }

}

 

Теперь, когда вы получили представление об итераторах, рассмотрим их более формально. Блок итератора синтаксически представляет собой обычный блок и может встречаться в теле метода, операции или части get свойства, если соответствующее возвращаемое значение имеет тип IEnumerable или IEnumerator.

В теле блока итератора могут встречаться две конструкции:

                    yieldreturn формирует значение, выдаваемое на очередной итерации;

                    yieldbreak сигнализирует о завершении итерации.

Ключевое слово yield имеет специальное значение для компилятора только в этих конструкциях.

Код блока итератора выполняется не так, как обычные блоки. Компилятор формирует служебный объект-перечислитель, при вызове метода MoveNext которого выполняется код блока итератора, выдающий очередное значение с помощью ключевого слова yield. Следующий вызов методаMoveNext объекта-перечислителя возобновляет выполнение блока итератора с момента, на котором он был приостановлен в предыдущий раз.

 

 

Лекция 16. Структуры и перечисления

 

Структуры

Структура — тип данных, аналогичный классу, но имеющий ряд важных отличий от него:

                    структура является значимым, а не ссылочным типом данных, то есть экземпляр структуры хранит значения своих элементов, а не ссылки на них, и располагается в стеке, а не в хипе;

                    структура не может участвовать в иерархиях наследования, она может только реализовывать интерфейсы;

                    в структуре запрещено определять конструктор по умолчанию, поскольку он определен неявно и присваивает всем ее элементам значения по умолчанию (нули соответствующего типа);

                    в структуре запрещено определять деструкторы, поскольку это бессмысленно.

Отличия от классов обуславливают область применения структур: типы данных, имеющие небольшое количество полей, с которыми удобнее работать как со значениями, а не как со ссылками.

Синтаксис структуры:

[ атрибуты ] [ спецификаторы ] struct имя_структуры [ : интерфейсы ]

    тело_структуры [ ; ]

 

Из спецификаторов доступа допускаются только public, internal и private (последний — только для вложенных структур). Интерфейсы, реализуемые структурой, перечисляются через запятую. Тело структуры может состоять из констант, полей, методов, свойств, событий, индексаторов, операций, конструкторов и вложенных типов. Правила их описания и использования аналогичны соответствующим элементам классов, за исключением некоторых отличий, вытекающих из упомянутых ранее:

Информация о работе Программирование