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

Автор работы: Пользователь скрыл имя, 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 Кб (Скачать документ)

Синтаксис интерфейса аналогичен синтаксису класса:

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

     тело_интерфейса [ ; ]

 

Для интерфейса могут быть указаны спецификаторы new, public, protected, internal и private. Спецификатор new применяется для вложенных интерфейсов и имеет такой же смысл, как и соответствующий модификатор метода класса. Остальные спецификаторы управляют видимостью интерфейса. По умолчанию интерфейс доступен только из сборки, в которой он описан (internal).

Интерфейс может наследовать свойства нескольких интерфейсов, в этом случае предки перечисляются через запятую. Тело интерфейса составляют абстрактные методы, шаблоны свойств и индексаторов, а также события.

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

interface IAction

{

    void Draw();

    int Attack(int a);

    void Die();

    int Power { get; }

}

 

В интерфейсе IAction заданы заголовки трех методов и шаблон свойства Power, доступного только для чтения. Если бы требовалось обеспечить возможность установки свойства, в шаблоне следовало указать ключевое слово set, например:

int Power { get; set; }

 

Отличия интерфейса от абстрактного класса:

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

                    интерфейс не может содержать полей и обычных методов — все элементы интерфейса должны быть абстрактными;

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

                    класс может иметь в списке предков несколько интерфейсов, при этом он должен определять все их методы.

Реализация интерфейса

В списке предков класса сначала указывается его базовый класс, если он есть, а затем через запятую интерфейсы, которые реализует этот класс. Например, реализация интерфейса IAction в классе Monster может выглядеть следующим образом:

using System;

namespace ConsoleApplication1

{

    interface IAction

    {

        void Draw();

        int Attack( int a );

        void Die();

        int Power { get; }

    }

 

    class Monster : IAction

    {

        public void Draw()

        {

            Console.WriteLine( "Здесь был " + name );

 

        }

        public int Attack( int ammo_ )

        {

            ammo -= ammo_;

            if ( ammo > 0 ) Console.WriteLine( "Ба-бах!" );

            else          ammo = 0;

            return ammo;

        }

 

        public void Die()

        {

            Console.WriteLine( "Monster " + name + " RIP" );

            health = 0;

        }

 

        public int Power

        {

            get

            {

                return ammo * health;

            }

        }

 

        …

}

 

Сигнатуры методов в интерфейсе и реализации должны полностью совпадать. Для реализуемых элементов интерфейса в классе следует указывать спецификатор public. К этим элементам можно обращаться как через объект класса, так и через объект типа соответствующего интерфейса:

Monster Vasia = new Monster( 50, 50, "Вася" );  // объект класса Monster

Vasia.Draw();                                   // результат: Здесь был Вася

IAction Actor = new Monster( 10, 10, "Маша" );  // объект типа интерфейса

Actor.Draw();                                   // результат: Здесь был Маша

 

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

    class Monster : IAction

    {

        int IAction.Power

        {

            get

            {

                return ammo * health;

            }

        }

 

        void IAction.Draw()

        {

            Console.WriteLine( "Здесь был " + name );

        }

        ...

    }

...

IAction Actor = new Monster( 10, 10, "Маша" );

Actor.Draw();                      // обращение через объект типа интерфейса

 

// Monster Vasia = new Monster( 50, 50, "Вася" );   

// Vasia.Draw();                                                     ошибка!

 

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

Работа с объектами через интерфейсы. Операции is и as

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

Результат операции равен true, если объект можно преобразовать к заданному типу, и false в противном случае. Операция обычно используется в следующем контексте:

if ( объект is тип )

{

    // выполнить преобразование "объекта" к "типу"

    // выполнить действия с преобразованным объектом

}

 

Допустим, мы оформили какие-то действия с объектами в виде метода с параметром типа object. Прежде чем использовать этот параметр внутри метода для обращения к методам, описанным в производных классах, требуется выполнить преобразование к производному классу. Для безопасного преобразования следует проверить, возможно ли оно, например так:

static void Act( object A )

{

    if ( A is IAction )

    {

        IAction Actor = (IAction) A;

        Actor.Draw();

    }

}

 

В метод Act можно передавать любые объекты, но на экран будут выведены только те, которые поддерживают интерфейс IAction, .

Недостатком использования операции is является то, что преобразование фактически выполняется дважды: при проверке и при собственно преобразовании. Более эффективной является другая операция — as. Она выполняет преобразование к заданному типу, а если это невозможно, формирует результат null, например:

static void Act( object A )

{

    IAction Actor = A as IAction;

    if ( Actor != null ) Actor.Draw();

}

 

Обе рассмотренные операции применяются как к интерфейсам, так и к классам.

Интерфейсы и наследование

Интерфейс может не иметь или иметь сколько угодно интерфейсов-предков, в последнем случае он наследует все элементы всех своих базовых интерфейсов, начиная с самого верхнего уровня.

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

Интерфейс, на собственные или унаследованные элементы которого имеется явная ссылка, должен быть указан в списке предков класса.

Класс наследует все методы своего предка, в том числе те, которые реализовывали интерфейсы. Он может переопределить эти методы с помощью спецификатора new, но обращаться к ним можно будет только через объект класса. Если использовать для обращения ссылку на интерфейс, вызывается не переопределенная версия:

interface IBase 

   void A(); 

 

class Base : IBase 

   public void A() { ... } 

 

class Derived: Base

   new public void A() { ... } 

 

...

Derived d = new Derived (); 

d.A();                        // вызывается Derived.A(); 

IBase id = d; 

id.A();                       // вызывается Base.A(); 

 

Однако если интерфейс реализуется с помощью виртуального метода класса, после его переопределения в потомке любой вариант обращения (через класс или через интерфейс) приведет к одному и тому же результату. Метод интерфейса, реализованный явным указанием имени, объявлять виртуальным запрещается.

Существует возможность повторно реализовать интерфейс, указав его имя в списке предков класса наряду с классом-предком, уже реализовавшим этот интерфейс. При этом реализация переопределенных методов базового класса во внимание не принимается:

interface IBase 

   void A(); 

 

class Base : IBase 

   void IBase.A() { ... }        // не используется в Derived

 

class Derived : Base, IBase

   public void A() { ... } 

 

Если класс наследует от класса и интерфейса, которые содержат методы с одинаковыми сигнатурами, унаследованный метод класса воспринимается как реализация интерфейса. Вообще при реализации интерфейса учитывается наличие «подходящих» методов в классе независимо от их происхождения. Это могут быть методы, описанные в текущем или базовом классе, реализующие интерфейс явным или неявным образом.

 

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

 

В библиотеке классов .NET определено множество стандартных интерфейсов, задающих желаемое поведение объектов. Например, интерфейс IComparable задает метод сравнения объектов на больше-меньше, что позволяет выполнять их сортировку. Реализация интерфейсов IEnumerable и IEnumerator дает возможность просматривать содержимое объекта с помощью конструкции foreach, а реализация интерфейса ICloneable — клонировать объекты.

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

Сравнение объектов

Интерфейс IComparable определен в пространстве имен System. Он содержит всего один метод CompareTo, возвращающий результат сравнения двух объектов — текущего и переданного ему в качестве параметра:

interface IComparable

{

    int CompareTo( object obj )

}

 

Метод должен возвращать:

                    0, если текущий объект и параметр равны;

                    отрицательное число, если текущий объект меньше параметра;

                    положительное число, если текущий объект больше параметра.

Реализуем интерфейс IComparable в знакомом нам классе Monster. В качестве критерия сравнения объектов выберем поле health. В листинге 17.1 приведена программа, сортирующая массив монстров по возрастанию величины, характеризующей их здоровье.

Листинг 17.1. Пример реализации интерфейса IComparable

using System;

namespace ConsoleApplication1

{

    class Monster : IComparable

    {

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

        {

            this.health = health;

            this.ammo   = ammo;

            this.name   = name;

        }

               virtual public void Passport()

        {

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

                               name, health, ammo );

        }

         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()

        {

            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, "Маша" );

             Array.Sort( stado );               // сортировка стала возможной

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

        }

    }

}

 

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

Monster Маша     health = 40 ammo = 10

Monster Вася     health = 50 ammo = 50

Monster Петя     health = 80 ammo = 80

 

Во многих алгоритмах требуется выполнять сортировку объектов по различным критериям. В C# для этого используется интерфейс IComparer, который мы рассмотрим далее.


Сортировка по разным критериям (интерфейс IComparer)

Интерфейс IComparer определен в пространстве имен System.Collections. Он содержит один метод CompareTo, возвращающий результат сравнения двух объектов, переданных ему в качестве параметров:

interface IComparer

{

    int Compare ( object ob1, object ob2 )

}

 

Принцип применения этого интерфейса состоит в там, что для каждого критерия сортировки объектов описывается небольшой вспомогательный класс, реализующий этот интерфейс. Объект этого класса передается в стандартный метод сортировки массива в качестве второго аргумента.

Пример сортировки массива объектов из предыдущего листинга по именам (свойство Name, класс SortByName) и количеству вооружений (свойство Ammo, класс SortByAmmo) приведен в листинге 17.2.

Листинг 17.2. Сортировка по двум критериям

using System;

using System.Collections;

namespace ConsoleApplication1

{

    class Monster

    {

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

        {

            this.health = health;

            this.ammo   = ammo;

            this.name   = name;

        }

       

        public int Ammo

        {

            get { return ammo; }

            set

            {

                if (value > 0) ammo = value;

                else           ammo = 0;

            }

        }

 

        public string Name

        {

            get { return name; }

        }

 

        virtual public void Passport()

        {

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

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