Опубликован: 15.09.2010 | Доступ: свободный | Студентов: 5577 / 969 | Оценка: 3.97 / 3.80 | Длительность: 14:45:00
Лекция 5:

Классы: основные понятия

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >

Ключевое слово this

Каждый объект содержит свой экземпляр полей класса. Методы находятся в памяти в единственном экземпляре и используются всеми объектами совместно, поэтому необходимо обеспечить работу методов нестатических экземпляров с полями именно того объекта, для которого они были вызваны. Для этого в любой нестатический метод автоматически передается скрытый параметр this, в котором хранится ссылка на вызвавший функцию экземпляр. Этот процесс иллюстрирует рис. 5.5.

Передача методу скрытого параметра this

Рис. 5.5. Передача методу скрытого параметра this

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

class Demo
    {
        double y;
        public Demo T()         // метод возвращает ссылку на экземпляр
        {
            return this; 
        }
        public void Sety( double y )
        {
            this.y = y;         // полю y присваивается значение параметра y
        }
    }

Конструкторы

Конструктор предназначен для инициализации объекта. Он вызывается автоматически при создании объекта класса с помощью операции new. Имя конструктора совпадает с именем класса. Ниже перечислены свойства конструкторов

  • Конструктор не возвращает значение, даже типа void.
  • Класс может иметь несколько конструкторов с разными параметрами для разных видов инициализации.
  • Если программист не указал ни одного конструктора или какие-то поля не были инициализированы, полям значимых типов присваивается нуль, полям ссылочных типов — значение null.
  • Конструктор, вызываемый без параметров, называется конструктором по умолчанию.

До сих пор мы задавали начальные значения полей класса при описании класса. Это удобно в том случае, когда для всех экземпляров класса начальные значения некоторого поля одинаковы. Если же при создании объектов требуется присваивать полю разные значения, это следует делать в конструкторе. В листинге 5.6 в класс Demo добавлен конструктор, а поля сделаны закрытыми.

using System;
namespace ConsoleApplication1
{   
    class Demo
    {
        public Demo( int a, double y )          // конструктор с параметрами
        {
            this.a = a;
            this.y = y;
        }

        public double Gety()                    // метод получения поля y
        {
            return y; 
        }

        int a;   
        double y;
    }
    
    class Class1
    {   static void Main()
        {
            Demo a = new Demo( 300, 0.002 );     // вызов конструктора
            Console.WriteLine( a.Gety() );       // результат: 0,002
            Demo b = new Demo( 1, 5.71 );        // вызов конструктора
            Console.WriteLine( b.Gety() );       // результат: 5,71
        }
    }
}
Листинг 5.6. Класс с конструктором

Часто бывает удобно задать в классе несколько конструкторов, чтобы обеспечить возможность инициализации объектов разными способами. Все конструкторы должны иметь разные сигнатуры.

Если один из конструкторов выполняет какие-либо действия, а другой должен делать то же самое плюс еще что-нибудь, удобно вызвать первый конструктор из второго. Для этого используется уже известное вам ключевое слово this в другом контексте, например:

class Demo
    {
        public Demo( int a )                         // конструктор 1
        {
            this.a = a;
        }
        public Demo( int a, double y ) : this( a )   // вызов конструктора 1
        {
            this.y = y;
        }
        ...
    }

Конструкция, находящаяся после двоеточия, называется инициализатором.

Как вы помните, все классы в C# имеют общего предка — класс object. Конструктор любого класса, если не указан инициализатор, автоматически вызывает конструктор своего предка.

До сих пор речь шла об "обычных" конструкторах, или конструкторах экземпляра. Существует второй тип конструкторов — статические конструкторы, или конструкторы класса. Конструктор экземпляра инициализирует данные экземпляра, конструктор класса — данные класса.

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

Некоторые классы содержат только статические данные и, следовательно, создавать экземпляры таких объектов не имеет смысла. В версию 2.0 введена возможность описывать статический класс, то есть класс с модификатором static. Экземпляры такого класса создавать запрещено, и кроме того, от него запрещено наследовать. Все элементы такого класса должны явным образом объявляться с модификатором static (константы и вложенные типы классифицируются как статические элементы автоматически). В листинге 5.7 приведен пример статического класса.

using System;
namespace ConsoleApplication1
{
    static class D
    {
        static int a = 200;
        static double b = 0.002;

        public static void Print ()
        {
            Console.WriteLine( "a = " + a );
            Console.WriteLine( "b = " + b );
        }
    }

    class Class1
    {   static void Main()
        {
            D.Print();
        }
    }
}
Листинг 5.7. Статический класс (начиная с версии 2.0)

В качестве "сквозного" примера, на котором будет демонстрироваться работа с различными элементами класса, создадим класс, моделирующий персонажа компьютерной игры. Для этого требуется задать его свойства (например, количество щупальцев или наличие гранатомета) и поведение.

using System;
namespace ConsoleApplication1
{
    class Monster
    {
        public Monster()
        {
            this.name   = "Noname";
            this.health = 100;
            this.ammo   = 100;
        }

        public Monster( string name ) : this()
        {
            this.name = name;
        }

        public Monster( int health, int ammo, string name )
        {
            this.name   = name;
            this.health = health;
            this.ammo   = ammo;
        }

        public string GetName()
        {
            return name;
        }

        public int GetHealth()
        {
            return health;
        }

        public int GetAmmo() 
        {
            return ammo; 
        }

        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 X = new Monster();
            X.Passport();
            Monster Vasia = new Monster( "Vasia" );
            Vasia.Passport();
            Monster Masha = new Monster( 200, 200, "Masha" );
            Masha.Passport();
        }
    }
}
Листинг 5.8. Класс Monster

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

Monster Noname   health = 100 ammo = 100
Monster Vasia    health = 100 ammo = 100
Monster Masha    health = 200 ammo = 200

Свойства

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

[ атрибуты ] [ спецификаторы ] тип имя_свойства
{
    [ get код_доступа ]
    [ set код_доступа ]
}

Значения спецификаторов для свойств и методов аналогичны. Чаще всего свойства объявляются со спецификатором public. Код доступа представляет собой блоки операторов, которые выполняются при получении ( get ) или установке ( set ) свойства. Может отсутствовать либо часть get, либо set, но не обе одновременно.

Если отсутствует часть set, свойство доступно только для чтения (read-only), если отсутствует часть get, свойство доступно только для записи (write-only). В версии C# 2.0 введена возможность задавать разный уровень доступа для частей get и set.

Пример описания свойств:

public class Button: Control  
{
    private string caption;     // закрытое поле, с которым связано свойство 
    public string Caption {     // свойство
       get {                    // способ получения свойства
          return caption;
       }
       set {                      // способ установки свойства
          if (caption != value) {
             caption = value;
          }
       }
    }
    ...
}

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

В программе свойство выглядит как поле класса, например:

Button ok = new Button();
ok.Caption = "OK";              // вызывается метод установки свойства  
string s = ok.Caption;          // вызывается метод получения свойства

При обращении к свойству автоматически вызываются указанные в нем методы чтения и установки.

Синтаксически чтение и запись свойства выглядят почти как методы. Метод get должен содержать оператор return. В методе set используется параметр со стандартным именем value, который содержит устанавливаемое значение.

Добавим в класс Monster, описанный в листинге 5.8, свойства, позволяющие работать с закрытыми полями этого класса. Код класса несколько разрастется, зато упростится его использование.

using System;
namespace ConsoleApplication1
{
    class Monster
    {   public Monster()
        {
            this.health = 100;
            this.ammo   = 100;
            this.name   = "Noname";
        }

        public Monster( string name ) : this()
        {
            this.name = name;
        }

        public Monster( int health, int ammo, string name )
        {
            this.health = health;
            this.ammo   = ammo;
            this.name   = name;
        }

        public int Health          // свойство Health связано с полем health
        {
            get 
            {
                return health;
            }
            set 
            {
                if (value > 0) health = value;
                else           health = 0;
            }
        }

        public int Ammo                // свойство Ammo связано с полем ammo
        {
            get 
            {
                return ammo;
            }
            set 
            {
                if (value > 0) ammo = value;
                else           ammo = 0;
            }
        }

        public string Name             // свойство Name связано с полем name
        {
            get 
            {
                return name;
            }
        }

        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 Masha = new Monster( 200, 200, "Masha" );
            Masha.Passport();
            --Masha.Health;              // использование свойств
            Masha.Ammo += 100;           // использование свойств
            Masha.Passport();
        }
    }
}
Листинг 5.9. Класс Monster со свойствами

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

Monster Masha    health = 200 ammo = 200
Monster Masha    health = 199 ammo = 300

Вопросы и задания для самостоятельной работы студента

  1. Перечислите и опишите элементы класса в C#.
  2. Опишите способы передачи параметров в методы.
  3. Запишите алгоритм вычисления чисел Фибоначчи с помощью рекурсии.
  4. Для чего в классе может потребоваться несколько конструкторов?
  5. Как можно вызвать один конструктор из другого? Зачем это нужно?
  6. Что такое this? Что в нем хранится, как он используется?
  7. Что такое деструктор? Гарантирует ли среда его выполнение?
  8. Какие действия обычно выполняются в части set свойства?
  9. Может ли свойство класса быть не связанным с его полями?
  10. Можно ли описать разные спецификаторы доступа к частям get и set свойства?

Лабораторные работы

Лабораторная работа № 4. Простейшие классы

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

Задание

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

Написать программу, демонстрирующую все разработанные элементы класса.

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
Георгий Кузнецов
Георгий Кузнецов

"Сокрытие деталей реализации называется инкапсуляцией (от слова "капсула"). "

Сколько можно объяснять?!

ИНКАПСУЛЯЦИЯ НЕ РАВНА СОКРЫТИЮ!!!

Инкапсуляция это парадигма ООП, которая ОБЕСПЕЧИВАЕТ СОКРЫТИЕ!!!

НО СОКРЫТИЕМ  НЕ ЯВЛЯЕТСЯ!!! 

Если буровая коронка обеспечивает разрушение породы, то является ли она сама разрушением породы? Конечно нет!

Ольга Притоманова
Ольга Притоманова