МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФ

Федеральное государственное бюджетное образовательное учреждение высшего образования

«ДАГЕСТАНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»

Юридический институт

 

Кафедра информационного права и информатики

 

 

Бахмудов Бегамагомед Ахмедович

 

 

 

 

Основы объектно-ориентированного программирования

 

 

 

 

 

Электронный курс лекций

 

для обучающихся по специальности 09.03.03 – «Прикладная информатика»

Профиль подготовки: Прикладная информатика в юриспруденции

 

 

Содержание

 

Tема 1. Основные понятия ООП

Тема 2. Классы в C#. Объявление классов и создание объектов

Тема 3 Наследование в Си-шарп. Конструктор базового класса

Тема 4 Конструкторы в C#. Указатель this

Тема 5 Свойства в Си-шарп. Аксессоры get и set. Автоматические свойства

Тема 6 Наследование в Си-шарп. Конструктор базового класса

 

 

 

 

 

 

 


 

Т.1 Основные понятия ООП

 

История возникновения ООП

ООП возникло в результате развития идеологии процедурного программирования, где данные и подпрограммы (процедуры, функции) их обработки формально не связаны. Для дальнейшего развития объектно-ориентированного программирования большое значение имели понятия события (в рамках событийно-ориентированного программирования)  и компонента (в контексте компонентного программирования).

 

Первым языком программирования, в котором были предложены основные понятия, впоследствии сложившиеся в парадигму, была Симула, но термин «объектная ориентированность» не использовался в контексте использования этого языка. В момент его появления в 1967 году в нём были предложены революционные идеи: объекты, классы, виртуальные методы и др., однако это всё не было воспринято современниками как нечто грандиозное. Фактически, Симула была «Алголом с классами» (алгоритмический язык), упрощающим выражение в процедурном программировании многих сложных концепций. Понятие класса в Симуле может быть полностью определено через композицию конструкций Алгола (то есть класс в Симуле — это нечто сложное, описываемое посредством примитивов).

 

Взгляд на программирование «под новым углом» (отличным от процедурного) предложили Алан Кэй и Дэн Ингаллс в языке Smalltalk. Здесь понятие класса стало основообразующей идеей для всех остальных конструкций языка (то есть класс в Смолтоке является примитивом, посредством которого описаны более сложные конструкции). Именно он стал первым широко распространённым объектно-ориентированным языком программирования.

 

В настоящее время количество прикладных языков программирования (список языков), реализующих объектно-ориентированную парадигму, является наибольшим по отношению к другим парадигмам. Наиболее распространённые в промышленности языки (С++, Delphi, C#, Java и др.) воплощают объектную модель Симулы. Примерами языков, опирающихся на модель Смолтока, являются Python, Ruby.

 

ООП имеет уже более чем сорокалетнюю историю, но, несмотря на это, до сих пор не существует чёткого общепринятого определения данной технологии. Основные принципы, заложенные в первые объектные языки и системы, подверглись существенному изменению (или искажению) и дополнению при многочисленных реализациях последующего времени. Кроме того, примерно с середины 1980-х годов термин «объектно-ориентированный» стал модным, в результате с ним произошло то же самое, что несколько раньше с термином «структурный» (ставшим модным после распространения технологии структурного программирования) — его стали искусственно «прикреплять» к любым новым разработкам, чтобы обеспечить им привлекательность. Бьёрн Страуструп в 1988 году писал, что обоснование «объектной ориентированности» чего-либо, в большинстве случаев, сводится к ложному силлогизму: «— это хорошо. Объектная ориентированность — это хорошо. Следовательно, X является объектно-ориентированным».

Доцент кафедры Орегонского университета (США) Тимоти Бадд пишет:

Роджер Кинг аргументированно настаивал, что его кот является объектно-ориентированным. Кроме прочих своих достоинств, кот демонстрирует характерное поведение, реагирует на сообщения, наделён унаследованными реакциями и управляет своим, вполне независимым, внутренним состоянием.

По мнению Алана Кея, создателя языка Smalltalk, которого считают одним из «отцов-основателей» ООП, объектно-ориентированный подход заключается в следующем наборе основных принципов (цитируется по вышеупомянутой книге Т. Бадда).

1.       Всё является объектом.

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

3.       Каждый объект имеет независимую память, которая состоит из других объектов.

4.       Каждый объект является представителем класса, который выражает общие свойства объектов (таких, как целые числа или списки).

5.       В классе задаётся поведение (функциональность) объекта. Тем самым все объекты, которые являются экземплярами одного класса, могут выполнять одни и те же действия.

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

 

Объектно-ориентированное программирование (ООП) - методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.

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

 

Центральные понятия ООП:

-          Объект

-          Класс

 

При использовании данной методологии главными элементами программ являются объекты.

В реальном мире каждый предмет или процесс обладает набором статических и динамических характеристик (свойствами и поведением). Поведение объекта зависит от его состояния и внешних воздействий.

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

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

Класс - универсальный, комплексный тип данных, состоящий из тематически единого набора «полей» (переменных более элементарных типов) и «методов» (функций для работы с этими полями), то есть он является моделью информационной сущности с внутренним и внешним интерфейсами для оперирования своим содержимым (значениями полей). В частности, в классах широко используются специальные блоки из одного или чаще двух спаренных методов, отвечающих за элементарные операции с определенным полем (интерфейс присваивания и считывания значения), которые имитируют непосредственный доступ к полю. Эти блоки называются «свойствами» и почти совпадают по конкретному имени со своим полем (например, имя поля может начинаться со строчной, а имя свойства – с заглавной буквы). Другим проявлением интерфейсной природы класса является то, что при копировании соответствующей переменной через присваивание, копируется только интерфейс, но не сами данные, то есть класс – ссылочный тип данных. Переменная-объект, относящаяся к заданному классом типу, называется экземпляром этого класса. При этом в некоторых исполняющих системах класс также может представляться некоторым объектом при выполнении программы посредством динамической идентификации типа данных. Обычно классы разрабатывают таким образом, чтобы обеспечить отвечающие природе объекта и решаемой задаче целостность данных объекта, а также удобный и простой интерфейс. В свою очередь, целостность предметной области объектов и их интерфейсов, а также удобство их проектирования, обеспечивается наследованием.

Основные принципы ООП:

-          Абстракция

-          Инкапсуляция

-          Наследование

-          Полиморфизм

 

Абстра́кция — в объектно-ориентированном программировании это придание объекту характеристик, которые отличают его от всех других объектов, четко определяя его концептуальные границы. Основная идея состоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов, подобно тому, как функциональная абстракция разделяет способ использования функции и деталей её реализации в терминах более примитивных функций, таким образом, данные обрабатываются функцией высокого уровня с помощью вызова функций низкого уровня. Такой подход является основой объектно-ориентированного программирования. Это позволяет работать с объектами, не вдаваясь в особенности их реализации. В каждом конкретном случае применяется тот или иной подход: инкапсуляция, полиморфизм или наследование. Например, при необходимости обратиться к скрытым данным объекта, следует воспользоваться инкапсуляцией, создав, так называемую, функцию доступа или свойство. Абстракция данных — популярная и в общем неверно определяемая техника программирования. Фундаментальная идея состоит в разделении несущественных деталей реализации подпрограммы и характеристик существенных для корректного ее использования. Такое разделение может быть выражено через специальный «интерфейс», сосредотачивающий описание всех возможных применений программы.

 

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

 

Насле́дование — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с инкапсуляцией, полиморфизмом и абстракцией), позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом. Другими словами, класс-наследник реализует спецификацию уже существующего класса (базовый класс). Это позволяет обращаться с объектами класса-наследника точно так же, как с объектами базового класса. Простое наследование: Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class). В некоторых языках используются абстрактные классы. Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля, методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник вуза», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза».

 

Полиморфи́зм — возможность объектов с одинаковой спецификацией иметь различную реализацию. Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию — например, реализация класса может быть изменена в процессе наследования[1]. Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций». Полиморфизм — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, инкапсуляцией и наследованием). Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному — интерфейс, класс. Общность имеет внешнее и внутреннее выражение: внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством); внутренняя общность — одинаковая функциональность методов. Её можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (перегрузкой функций, перегрузкой операций).

 

Достоинства ООП

n  использование при программировании понятий, близких к предметной области;

n  возможность успешно управлять большими объемами исходного кода благодаря инкапсуляции, то есть скрытию деталей реализации объектов и упрощению структуры программы;

n  возможность многократного использования кода за счет наследования;

n  сравнительно простая возможность модификации программ;

n  возможность создания и использования библиотек объектов.

 

Недостатки ООП

n  некоторое снижение быстродействия программы, связанное с использованием виртуальных методов;

n  идеи ООП не просты для понимания и в особенности для  практического использования;

n  для эффективного использования существующих объектно-ориентированных систем требуется большой объем первоначальных знаний;

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

 

 

Тема2. Классы в C#. Объявление классов и создание объектов

 

Класс – это абстрактный тип данных. Другими словами, класс – это некоторый шаблон, на основе которого будут создаваться его экземпляры – объекты.

В
C# классы объявляются с помощью ключевого слова class. Общая структура объявления выглядит следующим образом:

[модификатор доступа] class [имя_класса] [ : предки ]  
{
   //тело класса
}


Модификаторов доступа для классов есть несколько. Основными из них являются:

-
 public – доступ к классу возможен из любого места одной сборки либо из другой сборки, на которую есть ссылка;
-
 internal – доступ к классу возможен только из сборки, в которой он объявлен.

 

Также, могут использованы следующие модификаторы:
-
protected - Используется для вложенных классов. Доступ только из элементов данного и производных классов

- private - (для вложенных классов). Доступ только из элементов класса, внутри которого описан данный класс.

- abstract - Абстрактный класс. Применяется в иерархиях

- sealed - Бесплодный класс. Применяется в иерархиях

- static - Статический класс.


Что такое сборка?

Сборка (
assembly) – это готовый функциональный модуль в виде exe либо dll файла (файлов), который содержит скомпилированный код для .NET. Сборка предоставляет возможность повторного использования кода.

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

Класс следует объявлять внутри пространства имен
 namespace, но за пределами другого класса (возможно также объявление класса внутри другого - вложенные типы, но это тема отдельного урока). 

Пример объявления классов
 Student и Pupil:

namespace HelloWorld
{
   class Student //без указания модификатор доступа, класс будет internal
   {
     //тело класса
   }
   public class Pupil
  {
    //тело класса
  }
}


Члены класса

Классы в C# могут содержать следующие члены:

- поля;
- константы;
- свойства;
- конструкторы;
- методы;
- события;
- операторы;
- индексаторы;
- вложенные типы.


Все члены класса, как и сам класс, имеют свой уровень доступа:
- public – доступ к члену возможен из любого места одной сборки, либо из другой сборки, на которую есть ссылка;
- protected – доступ к члену возможен только внутри класса, либо в классе-наследнике (при наследовании);
- internal – доступ к члену возможен только из сборки, в которой он объявлен;

-
 private – доступ к члену возможен только внутри класса;
-
 protected internal - доступ к члену возможен из одной сборки, либо из класса-наследника другой сборки.

Не указав модификатор доступа для члена, по умолчанию ему будет присвоен режим
 private.

При помощи модификаторов доступа в
C# реализуется один из базовых принципов ООП  инкапсуляция.

В этом уроке мы рассмотрим такие члены класса, как поля и константы.

Поля класса

Поле
 – это переменная, объявленная внутри класса. Как правило, поля объявляются с модификаторами доступа private либо protected, чтобы запретить прямой доступ к ним. Для получения доступа к полям следует использовать свойства или методы.

Пример объявления полей в классе:

class Student
{
   private string firstName;
   private string lastName;
   private int age;
   public string group; // не рекомендуется использовать public для поля
}


Создание объектов

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

namespace HelloWorld
{
   class Student
   {
     private string firstName;
     private string lastName;
     private int age;
     public string group;
   }
   class Program
   {
     static void Main(string[] args)
     {
       Student student1 = new Student(); //создание объекта student1 класса Student
       Student student2 = new Student();
     }
   }
}


Доступ к членам объекта осуществляется при помощи оператора точка «.» :

static void Main(string[] args)
{
   Student student1 = new Student();
   Student student2 = new Student();

   student1.group = "Group1";
   student2.group = "Group2";

   Console.WriteLine(student1.group); // выводит на экран "Group1"
   Console.Write(student2.group);
   Console.ReadKey();
}


Такие поля класса
 Student, как firstName, lastName и age указаны с модификатором доступа private, поэтому доступ к ним будет запрещен вне класса:

static void Main(string[] args)
{
   Student student1 = new Student(); 
   student1.firstName= "Nikolay"; //ошибка, нет доступа к полю firstName.
Программа не скомпилируется 
}


Константы
Константы-члены класса
 ничем не отличаются от простых констант внутри методов, которые рассматривались в уроке переменные и константы. 

Константа
 – это переменная, значений которой нельзя изменить. Константа объявляется с помощью ключевого слова const. Пример объявления константы:

class Math
{
   private const double Pi = 3.14;
}


Самостоятельное задание
С
оздайте какой-нибудь класс (например класс Автомобиль), объявите в нем поля с различными режимами доступа (
private, protected, internal, public). Создайте объект класса, и попытайтесь записать/получить данные с различных полей.

 

 

Т.3 Наследование в Си-шарп. Конструктор базового класса

 

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

Объявление нового класса, который будет наследовать другой класс, выглядит так:

class [имя_класса] : [имя_базового_класса]
{
   // тело класса
}


Пример использования наследования. На основе базового класса Животное создаются два класса Собака и Кошка, в эти два класса переходит свойство Имя животного:

class Animal
{
   public string Name { get; set; }
}
class Dog : Animal
{
   public void Guard()
   {
     // собака охраняет
   }
}
class Cat : Animal
{
   public void CatchMouse()
   {
     // кошка ловит мышь
   } 
}
class Program
{
 
  static void Main(string[] args)
   {
     Dog dog1 = new Dog();
     dog1.Name = "Барбос"; // называем пса
     Cat cat1 = new Cat();
     cat1.Name = "Барсик"; // называем кота
     dog1.Guard(); // отправляем пса охранять
     cat1.CatchMouse(); // отправляем кота на охоту 
   } 
}

 
Вызов конструктора базового класса в Си-шарп

В
базовом классе и классе-наследнике могут быть объявлены конструкторы, и тут возникает вопрос – какой конструктор за что должен отвечать, и как их вызывать. Логично будет сказать то, что конструктор базового класса будет создавать ту часть объекта, которая принадлежит базовому классу (ведь из базового класса о наследнике ничего неизвестно), а конструктор из наследника будет создавать свою часть.

Когда конструктор определен только в наследнике, то здесь всё просто – при создании объекта сначала вызывается конструктор по умолчанию базового класса, а затем конструктор наследника.

Когда конструкторы объявлены и в базовом классе, и в наследнике – нам необходимо вызывать их оба. Для вызова конструктора базового класса используется ключевое слово
 base. Объявление конструктора класса-наследника с вызовом базового конструктора имеет следующую структуру:

[имя_конструктора_класса-наследника] ([аргументы]) : base ([аргументы])
{
   // тело конструктора
}


В базовый конструктор передаются все необходимые аргументы для создания базовой части объекта.

Пример вызова базового конструктора. Есть тот же класс
 Животное и класс Попугай. В классе Животное есть только свойство Имя, и конструктор, который позволяет установить это имя. В классе Попугай есть свойство Длина клюва и конструктор, в котором мы задаем эту длину. При создании объекта Попугай мы указываем два аргумента – имя и клюв, и дальше аргумент Имя передается в базовый конструктор, он вызывается, и после его работы выполнение передается конструктору класса Попугай, где устанавливается длина:

 

 

class Animal
{
   public string Name { get; set; }

   public Animal(string name)
   {
     Name = name;
   } 
}
class Parrot : Animal
{
   public double BeakLength { get; set; } // длина клюва

   public Parrot(string name, double beak) : base(name)
   {
     BeakLength = beak;
   }
}
class Dog : Animal
{
  public Dog(string name) : base (name)
   { 
     // здесь может быть логика создания объекта Собака 
   }

class Program

   static void Main(string[] args)
  {
     Parrot parrot1 = new Parrot("Кеша", 4.2); 
     Dog dog1 = new Dog("Барбос"); 
   } 
}


Доступ к членам базового класса из класса-наследника

З
десь стоит отметить, что в классе-наследнике мы можем получить доступ к членам базового класса которые объявлены как public, protected, internal иprotected internal. Члены базового класса с модификатором доступа private также переходят в класс-наследник, но к ним могут иметь доступ только члены базового класса. Например, свойство, объявленное в базовом классе, которое управляет доступом к закрытому полю, будет работать корректно в классе-наследнике, но отдельно получить доступ к этому полю из класса-наследника мы не сможем.

Самостоятельное задание

С
оздайте базовый класс
 Геометрическая фигура, предусмотрите в нем общие поля/свойства, например координаты центра фигуры, с помощью конструктора должна быть возможность задать центр. На базе этого класса создайте два новых – Треугольник и Окружность. В этих классах должны быть свои особые поля, например радиус для окружности. В оба класса добавьте метод Нарисовать, в котором могла бы быть специфическая логика рисования фигуры. Создайте объекты треугольник и окружность.

 

 

Т. 4 Конструкторы в C#. Указатель this

 

Конструктор – это метод класса, предназначенный для инициализации объекта при его создании. Инициализация – это задание начальных параметров объектов/переменных при их создании.

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

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

Объявление конструктора имеет следующую структуру:

public [имя_класса] ([аргументы])
{
  // тело конструктора
}


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

class Car
{
   private double mileage;
   private double fuel;

   public Car() //объявление конструктора
   {
     mileage = 0;
     fuel = 0;
   }
}
class Program
{
   static void Main(string[] args)
   {
     Car newCar = new Car(); // создание объекта и вызов конструктора 
   } 
}


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

Конструктор также может иметь параметры.

Пример с тем же автомобилем, только теперь при создании объекта мы можем задать любые начальные значения:

class Car
{
   private double mileage;
   private double fuel;

   public Car(double mileage, double fuel)
   {
     this.mileage = mileage;
     this.fuel = fuel;
   }
}
class Program
{
   static void Main(string[] args)
   {
     Car newCar = new Car(100, 50); //вызов конструктора с параметрами 
   } 
}


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

В
примере выше используется ключевое слово this. 
Указатель
this - это указатель на объект, для которого был вызван нестатический метод. Ключевое слово this обеспечивает доступ к текущему экземпляру класса. Классический пример использования this, это как раз в конструкторах, при одинаковых именах полей класса и аргументов конструктора. Ключевое слово this это что-то вроде имени объекта, через которое мы имеем доступ к текущему объекту. 

Несколько конструкторов

В
классе возможно указывать множество конструкторов, главное чтобы они отличались сигнатурами.
 Сигнатура, в случае конструкторов, - это набор аргументов. Например, нельзя создать два конструктора, которые принимают два аргумента типа int.

Пример использования нескольких конструкторов:

class Car
{
   private double mileage;
   private double fuel;

   public Car()
   {
     mileage = 0;
     fuel = 0;
   }

  public Car(double mileage, double fuel)
  {
     this.mileage = mileage;
     this.fuel = fuel;
  }
}
class Program
{
 
   static void Main(string[] args)
   {
     Car newCar = new Car(); // создаем автомобиль с параметрами по умолчанию, 0 и 0 
     Car newCar2 = new Car(100, 50); // создаем автомобиль с указанными параметрами 
  } 
}


Если в классе определен один или несколько конструкторов с параметрами, мы не сможем создать объект через неявный конструктор по умолчанию:

class Car
{
   private double mileage;
   private double fuel;

  public Car(double mileage, double fuel)
   {
     this.mileage = mileage;
     this.fuel = fuel;
   }
}
class Program

   static void Main(string[] args)
   {
     Car newCar = new Car(100, 50); 
     Car newCar2 = new Car(); // ошибка, в классе не определен конструктор без параметров 
  } 
}



Самостоятельное задание

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

 

Т.5 Свойства в Си-шарп. Аксессоры get и set. Автоматические свойства

 

Свойство в Си-шарп – это член класса, который предоставляет удобный механизм доступа к полю класса (чтение поля и запись). Свойство представляет собой что-то среднее между полем и методом класса. При использовании свойства, мы обращаемся к нему, как к полю класса, но на самом деле компилятор преобразовывает это обращение к вызову соответствующего неявного метода. Такой метод называется аксессор (accessor). Существует два таких метода: get (для получения данных) и set (для записи). Объявление простого свойства имеет следующую структуру:

[модификатор доступа] [тип] [имя_свойства]
{
   get
   {
     // тело аксессора для чтения из поля
   }

   set 
   {
     // тело аксессора для записи в поле
   }
}


Пример использования свойств. Имеется класс
 Студент, и в нем есть закрытое поле курс, которое не может быть ниже единицы и больше пяти. Для управления доступом к этому полю будет использовано свойство Year:

class Student
{
   private int year; //объявление закрытого поля

   public int Year //объявление свойства
   {
     get // аксессор чтения поля
     {
       return year;
     }
     set // аксессор записи в поле
     {
       if (value < 1)
         year = 1;
       else if (value > 5)
         year = 5;
       else year = value;
     }
   }
}
class Program
{
 
   static void Main(string[] args)
   {
     Student st1 = new Student();
     st1.Year = 0; // записываем в поле, используя аксессор set     

    Console.WriteLine(st1.Year); // читаем поле, используя аксессор get, выведет 1
     Console.ReadKey();
   } 
}


Проще говоря, в свойстве реализуются два метода. В теле аксессора
 getможет быть более сложная логика доступа, но в итоге должно возвращаться значение поля, либо другое значение с помощью оператора return. В аксессоре set же присутствует неявный параметр value, который содержит значение, присваиваемое свойству (в примере выше, при записи, значениеvalue равно «0»).

Зачем это нужно?

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

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

class Student
{
   private int year;

   public int GetYear()
   {
     return year;
   }
   public void SetYear(int value)
   {
     if (value < 1)
         year = 1;
     else if (value > 5)
         year = 5;
     else year = value;
   } 
}
class Program

   static void Main(string[] args)
   {
     Student st1 = new Student();
     st1.SetYear(0);
     Console.WriteLine(st1.GetYear());
     Console.ReadKey();
   } 
}


Свойство также может предоставлять доступ только на чтение поля или только на запись. Если, например, нам необходимо закрыть доступ на запись, мы просто не указываем аксессор
 set. Пример:

class Student
{
   private int year;

   public Student(int y) // конструктор
   {
     year = y;
   }

   public int Year
   {
     get
     {
       return year;
     } 
   }
}
class Program

   static void Main(string[] args)
   {
     Student st1 = new Student(2);

     Console.WriteLine(st1.Year); // чтение
     st1.Year = 5; // ошибка, свойство только на чтение 
     Console.ReadKey();
   } 
}


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

Автоматические свойства

Автоматическое свойство
 – это очень простое свойство, которое, в отличии от обычного свойства, уже определяет место в памяти (создает неявное поле), но при этом не позволяет создавать логику доступа. Структура объявления Автоматического свойства:

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


У таких свойств, у их аксессоров отсутствует тело.
Пример использования:

class Student
{
   public int Year { get; set; }
}
class Program

   static void Main(string[] args)
   {
     Student st1 = new Student();

     st1.Year = 0; 
     Console.WriteLine(st1.Year);
     Console.ReadKey();
   } 
}


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

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

public int Year { private get; set; } // свойство только на запись
public int Year { get; private set; } // свойство только на чтение



Самостоятельное задание

С
оздайте класс
 Телевизор, объявите в нем поле громкость звука, для доступа к этому полю реализуйте свойство. Громкость может быть в диапазоне от 0 до 100.

 

Т.6 Наследование в Си-шарп. Конструктор базового класса

 

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

Объявление нового класса, который будет наследовать другой класс, выглядит так:

class [имя_класса] : [имя_базового_класса]
{
   // тело класса
}


Пример использования наследования. На основе базового класса Животное создаются два класса Собака и Кошка, в эти два класса переходит свойство Имя животного:

class Animal
{
   public string Name { get; set; }
}
class Dog : Animal
{
   public void Guard()
   {
     // собака охраняет
   }
}
class Cat : Animal
{
   public void CatchMouse()
   {
     // кошка ловит мышь
   } 
}
class Program
{
 
  static void Main(string[] args)
   {
     Dog dog1 = new Dog();
     dog1.Name = "Барбос"; // называем пса
     Cat cat1 = new Cat();
     cat1.Name = "Барсик"; // называем кота
     dog1.Guard(); // отправляем пса охранять
     cat1.CatchMouse(); // отправляем кота на охоту 
   } 
}

 
Вызов конструктора базового класса в Си-шарп

В
базовом классе и классе-наследнике могут быть объявлены конструкторы, и тут возникает вопрос – какой конструктор за что должен отвечать, и как их вызывать. Логично будет сказать то, что конструктор базового класса будет создавать ту часть объекта, которая принадлежит базовому классу (ведь из базового класса о наследнике ничего неизвестно), а конструктор из наследника будет создавать свою часть.

Когда конструктор определен только в наследнике, то здесь всё просто – при создании объекта сначала вызывается конструктор по умолчанию базового класса, а затем конструктор наследника.

Когда конструкторы объявлены и в базовом классе, и в наследнике – нам необходимо вызывать их оба. Для вызова конструктора базового класса используется ключевое слово
 base. Объявление конструктора класса-наследника с вызовом базового конструктора имеет следующую структуру:

[имя_конструктора_класса-наследника] ([аргументы]) : base ([аргументы])
{
   // тело конструктора
}


В базовый конструктор передаются все необходимые аргументы для создания базовой части объекта.

Пример вызова базового конструктора. Есть тот же класс
 Животное и класс Попугай. В классе Животное есть только свойство Имя, и конструктор, который позволяет установить это имя. В классе Попугай есть свойство Длина клюва и конструктор, в котором мы задаем эту длину. При создании объекта Попугай мы указываем два аргумента – имя и клюв, и дальше аргумент Имя передается в базовый конструктор, он вызывается, и после его работы выполнение передается конструктору класса Попугай, где устанавливается длина:

 

 

class Animal
{
   public string Name { get; set; }

   public Animal(string name)
   {
     Name = name;
   } 
}
class Parrot : Animal
{
   public double BeakLength { get; set; } // длина клюва

   public Parrot(string name, double beak) : base(name)
   {
     BeakLength = beak;
   }
}
class Dog : Animal
{
  public Dog(string name) : base (name)
   { 
     // здесь может быть логика создания объекта Собака 
   }

class Program

   static void Main(string[] args)
  {
     Parrot parrot1 = new Parrot("Кеша", 4.2); 
     Dog dog1 = new Dog("Барбос"); 
   } 
}


Доступ к членам базового класса из класса-наследника

З
десь стоит отметить, что в классе-наследнике мы можем получить доступ к членам базового класса которые объявлены как public, protected, internal иprotected internal. Члены базового класса с модификатором доступа private также переходят в класс-наследник, но к ним могут иметь доступ только члены базового класса. Например, свойство, объявленное в базовом классе, которое управляет доступом к закрытому полю, будет работать корректно в классе-наследнике, но отдельно получить доступ к этому полю из класса-наследника мы не сможем.

Самостоятельное задание

С
оздайте базовый класс
 Геометрическая фигура, предусмотрите в нем общие поля/свойства, например координаты центра фигуры, с помощью конструктора должна быть возможность задать центр. На базе этого класса создайте два новых – Треугольник и Окружность. В этих классах должны быть свои особые поля, например радиус для окружности. В оба класса добавьте метод Нарисовать, в котором могла бы быть специфическая логика рисования фигуры. Создайте объекты треугольник и окружность.