Классы на разных уровнях представления программы.Основные понятия ООП.Объекты и их жизненный цикл.Свойства.Индексаторы.
Выбери формат для чтения
Загружаем конспект в формате docx
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Оглавление
Классы на разных уровнях представления программы 2
Основные понятия ООП 3
Объекты и их жизненный цикл 7
Свойства. 9
Индексаторы. 13
Классы на разных уровнях представления программы
Программу можно рассматривать на трёх уровнях:
• Логический (проектный уровень, уровень заказчика);
• Уровень языка программирования (уровень разработчика);
• Физический (организация программы в памяти).
На логическом уровне в основе ООП лежит выделение в решаемой задаче объектов, и описание соответствующих им классов. Каждый объект (экземпляр класса) характеризуется набором признаков, к которым относят свойства, выраженные атрибутами и поведение, описываемое операциями, выполняемыми объектом.
Для графического изображения классов в унифицированном языке моделирования, который используется для проектирования программ, класс изображается прямоугольником, разделённым на части как изображено на рисунке.
В верхнем разделе указывается имя класса. Следующий раздел содержит атрибуты класса. Атрибуты, помеченные знаком «-» относятся к закрытым (private), помеченные знаком «+» - к публичным (public), помеченные знаком «#» к защищённым (protected). Последний раздел содержит операции класса. Операции, помеченные знаком «-» относятся к публичным (private), помеченные знаком «+» - к закрытым (public), помеченные знаком «#» к защищённым (protected).
На уровне языка программирования для реализации объектно-ориентированных проектов вводятся классы, объекты (экземпляры классов). Класс содержит описание признаков объектов, которые носят название поля, методы и свойства.
На физическом уровне рассматривается организация объектов в памяти, организация и вызов методов.
Основные понятия ООП
Классом называется особый тип похожий на записи, который может иметь в своём составе поля, методы и свойства и другие элементы, которые мы рассмотрим позже. Его также называют пользовательским типом. В примере ниже описан класс Frac, реализующий абстракцию данных «простая дробь». Описание класса Frac, помещено в пространстве имён разрабатываемого проекта. Его можно физически разместить в том же файле, что и класс Program, или в отдельном файле. В одном файле размещают описание одного или нескольких логически связанных классов.
Ниже приведён пример организации класса для реализации простых дробей. Для хранения числителя и знаменателя дроби в состав класса добавлены два поля, и они сделаны открытыми (с уровнем видимости public).
class Frac
{
public int num;//Числитель.
public int den;//Знаменатель.
}
class Program
{
static void Main(string[] args)
{
Frac a = new Frac();//Создаём простую дробь.
Console.WriteLine("num = {0}", a.num);
Console.WriteLine("den = {0}", a.den);
a.num = 1;//Устанавливаем значение поля num.
a.den = 2;//Устанавливаем значение поля den.
Console.WriteLine("num = {0}", a.num);
Console.WriteLine("den = {0}", a.den);
}
}
Результат работы программы:
Однако в ООП не принято работать с полями напрямую. Их рекомендуется делать закрытыми, а доступ к ним обеспечивать посредством специальных методов.
class Frac
{
int num;//Числитель.
int den;//Знаменатель.
public int GetNum()//Взять числитель.
{
return num;
}
public void SetNum(int num_)//Установить числитель.
{
num = num_;
}
public int GetDen()//Взять знаменатель.
{
return den;
}
public void SetDen(int den_)//Установить знаменатель.
{
den = den_;
}
}
Класс Frac обеспечивает пользователя новым типом данных для работы с простыми дробями, которого нет в C#. Класс Frac имеет в своём составе: поля num (числитель дроби) и den (знаменатель дроби), методы GetNum (прочитать числитель), GetDen (прочитать знаменатель), SetNum (установить числитель), SetDen (установить знаменатель). По умолчанию поля описаны с уровнем доступа private, методы описаны с уровнем доступа public (публичный).
Где допустимо описание классов? Классы могут быть описаны:
• в пространстве имён проекта (файл с расширением .cs),
• в классе.
Для того чтобы использовать новый тип в программе, нужно объявить переменную этого типа. Переменная объектного типа называется экземпляром класса, или объектом:
class Program
{
static void Main(string[] args)
{
Frac a = new Frac();//Создаём простую дробь.
Console.WriteLine("num = {0}", a.GetNum());
Console.WriteLine("den = {0}", a.GetDen());
a.SetNum(1);//Устанавливаем значение поля num.
a.SetDen(2);//Устанавливаем значение поля den.
Console.WriteLine("num = {0}", a.GetNum());
Console.WriteLine("den = {0}", a.GetDen());
}
}
Результат работы программы:
Описанные переменные a типа Frac – представляет в тексте программы объект класса Frac.
На уровне физической памяти объект состоит из полей, которые описаны в его типе и содержат данные, уникальные для каждого объекта класса. Ниже на рисунке показано распределение памяти под объект типа Frac.
Рисунок. Организация объектов в памяти.
Для класса Frac родительским классом является класс object по умолчанию.
Ограничений на тип полей в классе не предусмотрено. Обычно поля используются для хранения данных. Со значениями полей оперируют методы класса.
В отличие от полей, набор методов класса один и является общим для всех объектов класса. Методы – это функции, описанные внутри класса и предназначенные для операций над полями. От обычных подпрограмм методы отличаются тем, что им при вызове передаётся (неявно) указатель на тот объект, который их вызвал. Внутри методов он доступен под зарезервированным именем this. Поскольку указатель на объект, вызвавший метод, передаётся методу всегда, при описании метода он не указывается в списке формальных параметров.
Понятие свойство мы определим позже. Пока его можно рассматривать как поле, доступное не напрямую, а через методы.
Объекты и их жизненный цикл
Объекты в C# могут быть только динамическими, т. е. создаются в свободной памяти (куче). Описание в программе переменной типа класс не приводит к автоматическому распределению памяти под объект класса при запуске приложения. Переменная типа класс, например Frac a;, на самом деле является указателем, содержащим адрес объекта (экземпляр класса).
Новый экземпляр объекта создаётся конструктором, а уничтожается специальным методом – деструктором:
Frac a = new Frac();//Создание объекта
Конструктор без параметров, который называется конструктором по умолчанию, автоматически создаётся компилятором для каждого класса. В C# у класса может быть несколько конструкторов. Имя конструктора должно совпадать с именем класса. Уничтожение объектов (для освобождения памяти занимаемой объектом) осуществляется автоматически программой, которая называется «сборщиком мусора». В деструкторе описываются действия, гарантирующие корректность последующего удаления объекта, например, проверяется, все ли ресурсы, используемые объектом, освобождены (файлы закрыты, удаленное соединение разорвано и т. п.).
Конструктор можно использовать для инициализации полей уже созданного объекта. Конструктор по умолчанию без параметров, для этой цели не подходит. В классе необходимо описать собственный конструктор с параметрами.
Пример работы с объектами класса Frac в режиме консольного приложения.
class Frac
{
int num;//Числитель.
int den;//Знаменатель.
public int GetNum()//Взять числитель.
{
return num;
}
public void SetNum(int num_)//Установить числитель.
{
num = num_;
}
public int GetDen()//Взять знаменатель.
{
return den;
}
public void SetDen(int den_)//Установить знаменатель.
{
den = den_;
}
//Конструктор с параметрами и со значениями по умолчанию.
public Frac(int num = 0, int den = 1)
{
this.num = num;
this.den = den;
}
//Создаём новый объект равный квадрату вызвавшего объекта.
public Frac Sq()
{
return new Frac(this.num * this.num, this.den * this.den);
}
//Возводим простую дробь в квадрат.
public void Sq_()
{
this.num = this.num * this.num;
this.den = this.den * this.den;
}
//Преобразуем простую дробь в строку.
public override string ToString()
{
return string.Format("{0}/{1}",num,den);
}
}
class Program
{
static void Main(string[] args)
{
Frac a = new Frac(1,2);//Создаём простую дробь.
Console.WriteLine("a = " + a.ToString());//Выводим результат.
a.Sq_();//Возводим дробь а в квадрат.
Console.WriteLine("a = " + a);//Выводим результат.
Frac b = a.Sq();//Возводим дробь b в квадрат.
Console.WriteLine("b = " + b);//Выводим результат.
Frac c = new Frac();
Console.WriteLine("c = " + c);//Выводим результат.
}
}
}
Результат работы программы:
Свойства.
Свойства определяют характеристики объектов класса в совокупности со способами их задания и получения, то есть методами записи и чтения.
Свойства служат для организации доступа к полям класса. Как правило, свойство связано с закрытым полем класса и определяет методы его получения и установки. Синтаксис свойства:
[ атрибуты ] [ спецификаторы ] тип имя_свойства
{
[ get код_доступа ]
[ set код_доступа ]
}
Значения спецификаторов для свойств и методов аналогичны. Чаще всего свойства объявляются со спецификатором public. Код доступа представляет собой блоки операторов, которые выполняются при получении ( get ) или установке ( set ) свойства. Может отсутствовать либо часть get, либо set, но не обе одновременно.
Если отсутствует часть set, свойство доступно только для чтения (read-only), если отсутствует часть get, свойство доступно только для записи (write-only). В версии C# 2.0 введена возможность задавать разный уровень доступа для частей get и set.
Метод записи обычно содержит действия по проверке допустимости устанавливаемого значения, метод чтения может содержать, например, поддержку счетчика обращений к полю.
В программе свойство выглядит как поле класса, например:
Button ok = new Button();
// вызывается метод установки свойства
ok.Caption = "OK";
// вызывается метод получения свойства
string s = ok.Caption;
При обращении к свойству автоматически вызываются указанные в нем методы чтения и установки.
Синтаксически чтение и запись свойства выглядят почти как методы. Метод get должен содержать оператор return. В методе set используется параметр со стандартным именем value, который содержит устанавливаемое значение.
В примере ниже вместо двух методов для чтения и записи поля мы используем одно свойство для чтения и записи этого поля. С помощью свойства мы можем контролировать диапазон значений, записываемых в поле.
class Frac
{
int num;//Числитель.
int den;//Знаменатель.
//Свойство для чтения и записи поля den.
public int Den
{
get{return den;}
set{
if (value != 0 ) den = value;
else throw new Exception("Ноль в знаменателе не допустим!");
}
}
//Свойство для чтения и записи поля num.
public int Num
{
get{return num;}
set{ num = value;}}
//Конструктор с параметрами и со значениями по умолчанию.
public Frac(int num = 0, int den = 1)
{
this.num = num;
this.den = den;
}
//Создаём новый объект равный квадрату вызвавшего объекта.
public Frac Sq()
{
return new Frac(this.num * this.num, this.den * this.den);
}
//Возводим простую дробь в квадрат.
public void Sq_()
{
this.num = this.num * this.num;
this.den = this.den * this.den;
}
//Преобразуем простую дробь в строку.
public override string ToString()
{
return string.Format("{0}/{1}",num,den);
}
}
class Program
{
static void Main(string[] args)
{
Frac a = new Frac(1,2);//Создаём простую дробь.
Console.WriteLine("a = " + a.ToString());//Выводим дробь.
a.Num = 5;
Console.WriteLine("num = " + a.Num);//Выводим числитель.
a.Den = 7;
Console.WriteLine("num = " + a.Den);//Выводим знаменатель.
Console.WriteLine("a = " + a);//Выводим дробь.
}
}
Результат работы программы:
Можно определить свойство только для записи, например, так:
class Frac
{
int num;//Числитель.
int den;//Знаменатель.
//Свойство для записи поля num.
public int Num
{
set
{
num = value;
}
}
//Свойство для записи поля den.
public int Den
{
set
{
if (value != 0) den = value;
else new Exception("Ноль в знаменателе не допустим!");
}
}
//Преобразуем простую дробь в строку.
public override string ToString()
{
return string.Format("{0}/{1}", num, den);
}
}
class Program
{
static void Main(string[] args)
{
Frac a = new Frac();//Создаём простую дробь.
Console.WriteLine("a = " + a.ToString());//Выводим дробь.
a.Num = 5;
Console.WriteLine("a = " + a);//Выводим дробь.
a.Den = 7;
Console.WriteLine("a = " + a);//Выводим дробь.
}
}
Результат работы программы:
Можно сделать свойство только для чтения, например, так:
class Frac
{
int num;//Числитель.
int den;//Знаменатель.
//Свойство для чтения поля num.
public int Num
{
get
{
return num;
}
}
//Свойство для чтения поля den.
public int Den { get { return den; } }
//Конструктор дроби с параметрами.
public Frac(int num, int den)
{
this.num = num; this.den = den;
}
}
class Program
{
static void Main(string[] args)
{
Frac a = new Frac(5,7);//Создаём простую дробь.
Console.WriteLine("a = " + a.Num);//Выводим числитель.
Console.WriteLine("a = " + a.Den);//Выводим знаменатель.
}
}
Результат работы программы:
Индексаторы.
Индексаторы обеспечивают возможность доступа к элементам класса по их порядковому номеру (индексу).
Индексатор представляет собой разновидность свойства. Если у класса есть скрытое поле, представляющее собой массив, то с помощью индексатора можно обратиться к элементу этого массива, используя имя объекта и номер элемента массива в квадратных скобках. Иными словами, индексатор — это такой "умный" индекс для объектов.
Синтаксис индексатора аналогичен синтаксису свойства:
[атрибуты] [спецификаторы] тип this [список_параметров]
{
get код_доступа
set код_доступа
}
Индексаторы чаще всего объявляются со спецификатором public, поскольку они входят в интерфейс объекта. Атрибуты и спецификаторы могут отсутствовать.
Код доступа представляет собой блоки операторов, которые выполняются при получении ( get ) или установке значения ( set ) элемента массива. Может отсутствовать либо часть get, либо set, но не обе одновременно. Если отсутствует часть set, индексатор доступен только для чтения (read-only), если отсутствует часть get, индексатор доступен только для записи (write-only).
Список параметров содержит одно или несколько описаний индексов, по которым выполняется доступ к элементу. Чаще всего используется один индекс целого типа.
Индексаторы в основном применяются для создания специализированных массивов, на работу с которыми накладываются какие-либо ограничения. Примеры работы с индексаторами приведён ниже.
Язык C# допускает использование многомерных индексаторов. Они описываются аналогично обычным и применяются в основном для контроля за занесением данных в многомерные массивы и выборке данных из многомерных массивов, оформленных в виде классов.
Ниже приведён пример класса Vector, объекты которого содержат одномерный массив целых переменных. Доступ к элементам массива для чтения и записи выполняется через индексатор.
class Vector
{
int[] v;
int size;
public int Size { get { return size; } }
public int this[int j]
{
get { return v[j]; }
set { v[j] = value; }
}
public Vector(int n = 5)
{
size = n;
v = new int[n];
}
public override string ToString()
{
string s = "";
foreach (int x in v) s += x + ", ";
return s;
}
}
class Program
{
static void Main(string[] args)
{
Vector a = new Vector(7);
for (int i = 0; i < a.Size; i++)
{
Console.Write("[{0}] = ", i);
a[i] = int.Parse(Console.ReadLine());
}
Console.WriteLine(a.ToString());
}
}
Результат работы программы: