1
Понятие объекта
В реальном мире каждый предмет или процесс обладает
свойствами и поведением (набором статических и
динамических характеристик). Поведение объекта зависит
от его состояния и внешних воздействий.
Понятие объекта в программе совпадает с обыденным
смыслом этого слова: объект представляется как
совокупность:
данных, характеризующих его состояние, и
функций их обработки, моделирующих его поведение.
Вызов функции на выполнение часто называют посылкой
сообщения объекту.
При создании объектно-ориентированной программы
предметная область представляется в виде совокупности
объектов. Выполнение программы состоит в том, что
объекты обмениваются сообщениями.
2
Пример ОО-программы Метод (функция) Запуск
class Двигатель класса Двигатель
{ public void Запуск()
{ Console.WriteLine( "пыщь пыщь" ); }
} Метод
Конструктор Запустить_двигатели
class Самолет класса Самолет класса Самолет
{ public Самолет()
{ левый = new Двигатель(); правый = new Двигатель(); }
public void Запустить_двигатели()
{ левый.Запуск(); правый.Запуск();_ } посылка сообщения
объекту правый
Двигатель левый, правый;
}
class Client Поля класса Самолет посылка сообщения
{ static void Main() объекту АН24_1
{ Самолет АН24_1 = new Самолет();
АН24_1.Запустить_двигатели();__ Результат работы
} программы:
} Объект АН24_1 пыщь пыщь
класса Самолет пыщь пыщь
3
Абстрагирование и инкапсуляция
При представлении реального объекта с помощью
программного необходимо выделить в первом его
существенные особенности и игнорировать
несущественные. Это называется абстрагированием.
Таким образом, программный объект — это абстракция.
Детали реализации объекта скрыты, он используется
через его интерфейс — совокупность правил доступа.
Скрытие деталей реализации называется инкапсуляцией.
Это позволяет представить программу в укрупненном
виде — на уровне объектов и их взаимосвязей, а
следовательно, управлять большим объемом информации.
Итак, объект — это инкапсулированная абстракция с
четко определенным интерфейсом.
encapsulation
encapsulation
4
Иллюстрация инкапсуляции
class Двигатель
{ public void Запуск()
{ Console.WriteLine( " пыщь пыщь " ); } Метод Main не
"взззззззззззз"
знает, как именно
}
запускаются
двигатели
class Самолет
{ public Самолет()
{ левый = new Двигатель(); правый = new Двигатель(); }
лев1 = …
public void Запустить_двигатели()
{ левый.Запуск(); правый.Запуск(); }
лев1.Запуск(); …
Двигатель левый, правый ; // скрытые поля данных
лев1, лев2, пр1, пр2;
}
class Class1
{ static void Main() Результат работы
{ Самолет АН24_1 = new Самолет(); программы:
взззззззззззз
АН24_1.Запустить_двигатели();
взззззззззззз
} взззззззззззз
} взззззззззззз
5
Наследование
Наследование (inheritance) - это процесс, посредством
которого один объект может приобретать свойства другого.
Важное значение имеет возможность многократного
использования кода. Для объекта можно определить
наследников, корректирующих или дополняющих его
поведение.
Наследование применяется для:
исключения из программы повторяющихся фрагментов кода;
упрощения модификации программы;
упрощения создания новых программ на основе существующих.
Благодаря наследованию появляется возможность
использовать объекты, исходный код которых недоступен, но
в которые требуется внести изменения.
Наследование позволяет создавать иерархии объектов.
Иерархия представляется в виде дерева, в котором более
общие объекты располагаются ближе к корню, а более
специализированные — на ветвях и листьях.
6
Пример иерархии: классы .NET для работы с потоками
object
DirectoryInfo
MarshalBy Ref Object
FileSystemInfo FileInfo
BinaryReader
Stream BufferedStream
BinaryWriter FileStream
MemoryStream
Directory
TextReader StreamReader
File
StringReader
TextWriter StreamWriter
StringWriter
7
Полиморфизм
Полиморфизм – это возможность использовать одно имя для
решения нескольких схожих, но технически разных задач,
или обращаться к объектам разного типа.
Идея полиморфизма: "один интерфейс, множество методов"
Пример:
в языке С: abs(), labs() и fabs(), в C# - Math.Abs()
Существует несколько видов полиморфизма:
перегрузка методов
Console.WriteLine
перегрузка операций
виртуальные методы операция +
для int и string
параметризованные классы
Чаще всего понятие полиморфизма связывают с механизмом
виртуальных методов.
8
Пример полиморфизма
Spirit
class Spirit
{ ...
virtual Act () { ... } Vampire Monster
}
class Monster: Spirit Daemon
{ ...
virtual Act () { ... } class Class1
} { Main
class Vampire: Spirit {
{ ... Spirit [] stado = new Spirit[100];
virtual Act () { ... } ... // занесение чудовищ в массив
}
class Daemon: Monster foreach (Spirit creature in stado)
{ ... creature.Act();
virtual Act () { ... } }
} }
9
Достоинства ООП
использование при программировании понятий,
близких к предметной области;
возможность успешно управлять большими объемами
исходного кода благодаря инкапсуляции, то есть
скрытию деталей реализации объектов и упрощению
структуры программы;
возможность многократного использования кода за
счет наследования;
сравнительно простая возможность модификации
программ;
возможность создания и использования библиотек
объектов.
10
Недостатки ООП
11
Понятие класса (повторение)
Класс является типом данных, определяемым
пользователем. Он должен представлять собой одну
логическую сущность, например, являться моделью
реального объекта или процесса. Элементами класса
являются данные и функции, предназначенные для их
обработки (методы).
Все классы .NET имеют общего предка — класс object, и
организованы в единую иерархическую структуру.
Классы логически сгруппированы в пространства имен,
которые служат для упорядочивания имен классов и
предотвращения конфликтов имен: в разных
пространствах имена могут совпадать. Пространства имен
могут быть вложенными.
Любая программа использует пространство имен System.
13
Описание класса
[ атрибуты ] [ спецификаторы ] class имя_класса [ : предки ]
тело_класса
Имя класса задается по общим правилам.
Тело класса — список описаний его элементов, заключенный
в фигурные скобки.
Атрибуты задают дополнительную информацию о классе.
Спецификаторы определяют свойства класса, а также
доступность класса для других элементов программы.
Простейшие примеры описания класса:
class Demo {} // пустой класс
public class Двигатель // класс с одним методом
{ public void Запуск()
{ Console.WriteLine( " пыщь пыщь " ); }
}
14
Спецификаторы класса
Спецификатор Описание
new (для вложенных классов). Задает новое описание класса взамен
унаследованного от предка. Применяется в иерархиях
public Доступ не ограничен
protected Используется для вложенных классов. Доступ
только из элементов данного и производных
классов
15
Элементы класса класс
локальные
типы
данные
функции
поля свойства
(переменные) константы
события
один набор на
каждый экземпляр
операции
конструкторы конструкторы
экземпляра
деструкторы
статические
конструкторы обычные
методы
методы методы
экземпляра класса
16
Сквозной пример класса
public int Health { // свойство
class Monster {
get { return health; }
public Monster() // конструкторы
set { if (value > 0) health = value;
{
else health = 0;
name = "Noname";
}
health = 100;
}
ammo = 100;
}
public void Passport() // метод
public Monster( string name ) : this()
{ Console.WriteLine(
{
"Monster {0} \t health = {1} \
this.name = name;
ammo = {2}", name, health, ammo );
}
}
public Monster( int health, int ammo,
string name )
{ public override string ToString(){
this.name = name; string buf = string.Format(
this.health = health; "Monster {0} \t health = {1} \
this.ammo = ammo; ammo = {2}", name, health, ammo);
} return buf; }
public string GetName() // метод
{ return name; } string name; // поле
public int GetAmmo() // метод int health, ammo; // поле
{ return ammo;} }
17
Описание объекта (экземпляра)
Класс является обобщенным понятием, определяющим
характеристики и поведение множества конкретных объектов
этого класса, называемых экземплярами (объектами) класса.
Объекты создаются явным или неявным образом (либо
программистом, либо системой). Программист создает экземпляр
класса с помощью операции new:
Demo a = new Demo();
Monster Vasia = new Monster();
Monster Petya = new Monster(“Петя“);
Для каждого объекта при его создании в памяти выделяется
отдельная область для хранения его данных.
Кроме того, в классе могут присутствовать статические
элементы, которые существуют в единственном экземпляре для
всех объектов класса.
Функциональные элементы класса всегда хранятся в
единственном экземпляре.
18
Пример создания объектов (экземпляров)
class Monster { ... }
class Class1
{
static void Main()
{
Monster X = new Monster();
X.Passport();
Monster Vasia = new Monster("Вася" );
Vasia.Passport();
Monster Masha = new Monster( 200, 200, "Маша" );
Сonsole.Writeline(Masha);
}
} Результат работы программы:
Monster Noname health = 100 ammo = 100
Monster Вася health = 100 ammo = 100
Monster Маша health = 200 ammo = 200
19
Присваивание и сравнение объектов (повторение)
Хип
Значение Значение Значение
Стек
Ссылка Ссылка Ссылка
а b c
Объекты
b = c
Величины ссылочного типа равны, если они ссылаются на
одни и те же данные (b == c, но a != b даже при равенстве
их значений или если обе ссылки равны null).
20
Данные: поля и константы
Данные, содержащиеся в классе, могут быть переменными или
константами.
Переменные, описанные в классе, называются полями класса.
При описании полей можно указывать атрибуты и спецификаторы,
задающие различные характеристики элементов:
21
Пример класса
using System;
namespace CA1
{ class Demo
{ public int a = 1; // поле данных
public const double c = 1.66; // константа
public static string s = "Demo"; // статическое поле класса
double y; // закрытое поле данных
}
class Class1
{ static void Main()
{
Demo x = new Demo(); // создание экземпляра класса Demo
Console.WriteLine( x.a ); // x.a - обращение к полю класса
Console.WriteLine( Demo.c ); // Demo.c - обращение к константе
Console.WriteLine( Demo.s ); // обращение к статическому полю
}}}
22
Спецификаторы полей и констант класса
Спецификатор Описание
new Новое описание поля, скрывающее унаследованный
элемент класса
public Доступ к элементу не ограничен
protected Доступ только из данного и производных классов
23
Методы
Метод — функциональный элемент класса, реализующий
вычисления или другие действия. Методы определяют
поведение класса и составляют его интерфейс.
Метод — законченный фрагмент кода, к которому можно
обратиться по имени. Он описывается один раз, а
вызываться может столько раз, сколько необходимо.
Один и тот же метод может обрабатывать различные
данные, переданные ему в качестве аргументов.
double a = 0.1;
double b = Math.Sin(a);
double c = Math.Sin(b-2*a);
Console.WriteLine(a);
24
Синтаксис метода
[ атрибуты ] [ спецификаторы ] тип имя_метода ( [ параметры ] ) тело_метода
class Demo
{ double y; // закрытое поле класса
26
Параметры методов
Параметры определяют множество значений аргументов, которые
можно передавать в метод.
Список аргументов при вызове как бы накладывается на список
параметров, поэтому они должны попарно соответствовать друг
другу.
Для каждого параметра должны задаваться его тип, имя и,
возможно, вид параметра.
Имя метода вкупе с количеством, типами и спецификаторами его
параметров представляет собой сигнатуру метода — то, по чему
один метод отличают от других.
В классе не должно быть методов с одинаковыми сигнатурами.
Метод, описанный со спецификатором static, должен обращаться
только к статическим полям класса.
Статический метод вызывается через имя класса, а
обычный — через имя экземпляра.
27
Пример
class Demo {
public int a = 1;
public const double c = 1.66;
static string s = "Demo";
double y;
public double Get_y() { return y; } // метод получения y
public void Set_y( double y_ ){ y = y_; } // метод установки y
public static string Get_s() { return s; } // метод получения s
}
class Client {
static void Main()
{ Demo x = new Demo();
x.Set_y(0.12); // вызов метода установки y
Console.WriteLine(x.Get_y()); // вызов метода получения y
Console.WriteLine(Demo.Get_s()); // вызов метода получения s
// Console.WriteLine(Get_s()); // вариант вызова из того же
}} // класса
28
Ключевое слово this
Чтобы обеспечить работу метода с полями того объекта, для которого
он был вызван, в метод автоматически передается скрытый параметр
this, в котором хранится ссылка на вызвавший функцию объект.
29
Использование явного this
В явном виде параметр this применяется:
30
Пример: счетчик (this)
class Counter
{ public bool Sync(out int x)
{ x = n;
return n==0 ? false : true ;
}
public void Set( int start ) { n = start; }
int n;
...
public void Set( int n ) { this.n = n; }
}
class Program
{ static void Main(string[] args)
{ Counter num1 = new Counter();
Counter num2 = new Counter();
...
int temp;
if ( num1.Sync(out temp) ) num2.Set(temp);
…
}} 31
Конструкторы
Конструктор – особый вид метода, предназначенный для
инициализации
объекта (конструктор экземпляра) или
32
Конструкторы экземпляра
Конструктор вызывается автоматически при создании
объекта класса с помощью операции new. Имя
конструктора совпадает с именем класса.
Свойства конструкторов
Конструктор не возвращает значение, даже типа void.
Класс может иметь несколько конструкторов с разными
параметрами для разных видов инициализации.
Если программист не указал ни одного конструктора или
какие-то поля не были инициализированы, полям
значимых типов присваивается нуль, полям ссылочных
типов — значение null.
Конструктор, вызываемый без параметров, называется
конструктором по умолчанию.
33
Пример класса с конструктором
class Demo
{
public Demo( int a, double y ) // конструктор
{
this.a = a;
this.y = y;
}
int a;
double y;
}
class Class1
{ static void Main()
{
Demo a = new Demo( 300, 0.002 ); // вызов конструктора
Demo b = new Demo( 1, 5.71 ); // вызов конструктора
…
}}
34
Пример класса с двумя конструкторами
class Demo
{
public Demo( int a ) // конструктор 1
{
this.a = a;
this.y = 0.002;
}
public Demo( double y ) // конструктор 2
{
this.a = 1;
this.y = y;
}
...
}
...
Demo x = new Demo( 300 ); // вызов конструктора 1
Demo y = new Demo( 5.71 ); // вызов конструктора 2
35
class Counter
Пример: счетчик (конструкторы)
{ public Counter() { }
public Counter(
public intint
Counter( nn) = 0)
{
this.n = n > 0 ? n : 0;
}
int n;
...
}
class Program
{ static void Main(string[] args)
{ Counter num1 = new Counter();
Counter num2 = new Counter(128);
...
}
}
36
Сквозной пример класса – 1/2
public int Health { // свойство
class Monster { get { return health; }
Monster
public Monster() // конструктор Vasia = new Monster();
set { if (value > 0) health = value;
{ Monster Petya = new Monster(“Петя“);
else health = 0;
}
name = "Noname"; Monster Masha = new Monster(“Мария”, 150, 3000);
health = 100; }
ammo = 100;
public void Passport() // метод
} { Console.WriteLine(
public Monster( string name ) : this() "Monster {0} \t health = {1} \
{ ammo = {2}", name, health, ammo );
this.name = name; }
}
public Monster(string name, int health, public override string ToString(){
int ammo) string buf = string.Format(
{ "Monster {0} \t health = {1} \
ammo = {2}", name, health, ammo);
this.name = name;
return buf; }
this.health = health > 0 ? health : 0 ;
this.ammo = ammo > 0 ? ammo : 0 ; string name;
} int health, ammo;
public string GetName() // метод }
{ return name; }
public int GetAmmo() // метод
{ return ammo;}
37
Сквозной пример класса – 2/2
public int Health { // свойство
class Monster { get { return health; }
set { if (value > 0) health = value;
public Monster(string name = "Noname", else health = 0;
int health = 100, int ammo = 100) }
{ }
this.name = name;
this.health = health > 0 ? health : 0 ; public void Passport() // метод
{ Console.WriteLine(
this.ammo = ammo > 0 ? ammo : 0 ;
"Monster {0} \t health = {1} \
} ammo = {2}", name, health, ammo );
public string GetName() // метод }
{ return name; }
public int GetAmmo() // метод public override string ToString(){
{ return ammo;} string buf = string.Format(
"Monster {0} \t health = {1} \
ammo = {2}", name, health, ammo);
return buf; }
string name;
Monster Vasia = new Monster(); int health, ammo;
Monster Petya = new Monster(“Петя“); }
Monster Masha = new Monster(“Мария”, 150, 3000);
38
Статические конструкторы
Статический конструктор (конструктор класса)
инициализирует статические поля класса.
Он не имеет параметров, его нельзя вызвать явным
образом. Система сама определяет момент, в который
требуется его выполнить. Гарантируется, что это
происходит до создания первого экземпляра объекта и до
вызова любого статического метода.
Статический конструктор, как и любой статический метод,
не имеет доступа к полям экземпляра.
39
Пример класса со статическими элементами
class Monster {
public Monster(string name = "Noname", int health = 100, int ammo = 100)
{
this.name = name; Monster Vasia = new Monster();
this.health = health > 0 ? health
Monster: Petya
0 ; = new Monster("Петя");
this.ammo = ammo > 0 ? ammo : 0 ;
Monster Masha = new Monster("Мария“,150, 300);
++num_Monsters;
} Console.WriteLine(Monster.Total()); // 3
}
static Monster() { num_Monsters = 0; } // избыточно, для примера
public static int Total() { return num_Monsters; }
...
string name; int health, ammo;
static int num_Monsters = 0;
}
40
Свойства
Свойства служат для организации доступа к полям класса. Как
правило, свойство определяет методы доступа к закрытому полю.
Свойства обеспечивают разделение между внутренним состоянием
объекта и его интерфейсом.
Синтаксис свойства:
[ спецификаторы ] тип имя_свойства
{
[ get код_доступа ]
[ set код_доступа ]
}
При обращении к свойству автоматически вызываются указанные в
нем блоки чтения (get) и установки (set).
Может отсутствовать либо часть get, либо set, но не обе
одновременно. Если отсутствует часть set, свойство доступно только
для чтения (read-only), если отсутствует get - только для записи
(write-only).
41
class Counter Пример: счетчик (свойства)
{ public Counter( int n = 0 )
{ this.n = n > 0 ? n : 0; }
public int N
{ get { return n; }
set { n = value > 0 ? value : 0; }
}
// или: set { if (value > 0) n = value; else throw new
Exception();}
int n; // поле, связанное со свойством N
}
class Program
{ static void Main(string[] args)
{ Counter num = new Counter();
num.N = 5; // работает set
int a = num.N; // работает get
num.N++; // работает get, а потом set
++num.N; // работает get, а потом set
42
Еще пример описания свойств
public class Button: Control
{ private string caption; // поле, с которым связано свойство
public string Caption { // свойство
get { return caption; } // способ получения свойства
43
Сквозной пример класса: свойства
class Monster {
public Monster(string name = "Noname", public int Health { // свойство
int health = 100, int ammo = 100) get { return health; }
{ set {health = value > 0 ? value : 0;
this.name = name; }
this.health = health > 0 ? health : 0 ;
}
this.ammo = ammo > 0 ? ammo : 0 ;
} public string Name { // свойство
get { return name; }
public string GetName() // метод }
{ return name; } public void Passport() // метод
public int GetAmmo() // метод { Console.WriteLine(
{ return ammo;} "Monster {0} \t health = {1} \
ammo = {2}", name, health, ammo );
}
public override string ToString(){
string buf = string.Format(
"Monster {0} \t health = {1} \
ammo = {2}", name, health, ammo);
return buf; }
string name;
int health, ammo;
}
44
Пример вычисляемых свойств
namespace ConsoleApplication1
{ class Ball {
public Ball(double radius, double density)
{ this.radius = radius;
this.density = density;
}
public double Mass
{ get { return radius * density; } } class Program
public double Diameter { static void Main(string[] args)
{ get { return radius * 2; } } {
Ball ball = new Ball(10, 6.2);
double radius; Console.WriteLine("Диаметр: " +
double density; ball.Diameter +
} " Масса: " + ball.Mass);
}}}
Перегрузка методов
Перегрузкой методов называется использование нескольких
методов с одним и тем же именем, но различными типами
параметров.
Компилятор определяет, какой именно метод требуется
вызвать, по типу фактических параметров. Это называется
разрешением (resolution) перегрузки.
// Возвращает наибольшее из двух целых:
int max( int a, int b )
// Возвращает наибольшее из трех целых:
int max( int a, int b, int c )
// Возвращает наибольшее из первого параметра
// и длины второго:
int max ( int a, string b )
...
Console.WriteLine( max( 1, 2 ) );
Console.WriteLine( max( 1, 2, 3 ) );
Console.WriteLine( max( 1, "2" ) );
Перегрузка методов является проявлением полиморфизма
46
class Counter
Пример: счетчик (перегрузка)
{ public Counter() { }
Перегружен-
public Counter( int n ) { this.n = n > 0 ? n : 0; } ные
конструкторы
public void Inc() { ++n; }
public void Inc( int delta ){ n += Math.Abs(delta); }
int n;
...
} Перегружен-
ные методы
class Program
{ static void Main(string[] args)
{ Counter num1 = new Counter();
Counter num2 = new Counter(128);
num1.Inc(4);
num1.Inc();
...
}
} 47
Сквозной пример класса
class Monster { public int Health { // свойство
public Monster() // конструктор get { return health; }
{ set { if (value > 0) health = value;
this.name = "Noname"; else health = 0;
this.health = 100; }
}
this.ammo = 100;
} public void Passport() // метод
public Monster( string name ) : this() { Console.WriteLine(
{ "Monster {0} \t health = {1} \
this.name = name; ammo = {2}", name, health, ammo );
} }
public Monster( int health, int ammo,
string name ) public override string ToString(){
{ string buf = string.Format(
"Monster {0} \t health = {1} \
this.name = name;
ammo = {2}", name, health, ammo);
this.health = health; return buf; }
this.ammo = ammo;
} string name;
public string GetName() // метод int health, ammo;
{ return name; } }
public int GetAmmo() // метод
{ return ammo;}
48
Операции класса (перегрузка операций)
В С# можно переопределить для своих классов действие
большинства операций. Это позволяет применять экземпляры
объектов в составе выражений аналогично переменным
стандартных типов:
MyObject a, b, c; ...
c = a + b; // операция сложения класса MyObject
Определение собственных операций класса называют
перегрузкой операций.
Операции класса описываются с помощью методов
специального вида (функций-операций):
public static имя_класса operator операция( параметры) {… }
Примеры заголовков:
public static int operator +( MyObject m ) // унарный плюс
public static MyObject operator --( MyObject m ) // декремент
class Monster {
public static Monster operator ++(Monster m)
{ Monster temp = new Monster(m.health+1, m.ammo, m.name);
return temp;
}
…
}
…
Monster vasia = new Monster();
++vasia; vasia++;
…
51
class Counter
Пример: счетчик (операция ++)
{ public Counter( int n = 0 ) { this.n = n > 0 ? n : 0; }
public static Counter operator ++(Counter param)
{
Counter temp = new Counter(param.n + 1);
return temp;
}
int n;
}
class Program
{ static void Main(string[] args)
{ Counter num = new Counter();
num++; ++num;
...
}
}
52
Правила оформления унарной операции класса
Параметр функции-операции должен иметь тип этого
класса.
Операция должна возвращать:
для операций +, -, ! и ~ величину любого типа;
для операций ++ и -- величину типа класса, для
которого она определяется.
Операции не должны изменять значение передаваемого
им операнда. Операция, возвращающая величину типа
класса, для которого она определяется, должна создать
новый объект этого класса, выполнить с ним необходимые
действия и передать его в качестве результата.
Префиксный и постфиксный инкремент/декремент не
различаются
53
Бинарные операции
Можно определять:
+ - * / % & | ^ << >> == != > < >= <=
54
Пример бинарных операций класса
class Monster {
public static Monster operator +( Monster m, int k )
{ Monster temp = new Monster(m.health+k, m.ammo, m.name);
return temp;
}
public static Monster operator +( int k, Monster m )
{ Monster temp = new Monster(m.health+k, m.ammo, m.name);
return temp;
}
…
}
…
Monster vasia = new Monster();
Monster masha = vasia + 10;
Monster petya = 5 + masha;
…
55
class Counter
Пример: счетчик (операция +)
{ ...
public static Counter operator +(Counter param, int delta)
{ Counter temp = new Counter(param.n + delta);
return temp;
}
public static Counter operator +(int delta, Counter param)
{ Counter temp = new Counter(param.n + delta);
return temp;
}
int n;
}
class Program
{ static void Main(string[] args)
{ Counter num = new Counter(); Counter num2 = new Counter();
num2 = num + 3; num2 = 3 + num;
...
} 56
Операции преобразования типа
Обеспечивают возможность явного и неявного преобразования между
пользовательскими типами данных.
implicit operator тип ( параметр ) // неявное преобразование
explicit operator тип ( параметр ) // явное преобразование
Выполняют преобразование из типа параметра в тип, указанный в
заголовке операции. Одним из этих типов должен быть класс, для
которого определяется операция. Таким образом, операции
выполняют преобразование либо типа класса к другому типу, либо
наоборот.
public static implicit operator int( Monster m )
{
return m.health;
}
public static explicit operator Monster( int h )
{
return new Monster( h, 100, "FromInt" );
} ...
Monster Masha = new Monster( 200, 200, "Masha" );
int i = Masha; // неявное преобразование
Masha = (Monster) 500; // явное преобразование
57
class Counter
Пример: счетчик (приведение типа)
{ ...
public static implicit operator int(Counter param)
{ return param.n; }
public static implicit operator Counter(int n)
{ return new Counter(n); }
int n;
}
class Program
{ static void Main(string[] args)
{ Counter num = new Counter();
int a = num;
num = 4;
a = num + 3;
;
...
}
} 58
Применение операций преобразования
Неявное преобразование выполняется автоматически:
при присваивании объекта переменной целевого типа;
59
Summary
60
Интерфейс класса
При создании класса следует хорошо продумать его
интерфейс — средства работы с классом, доступные
использующим его программистам.
Интерфейс хорошо спроектированного класса интуитивно
ясен, непротиворечив и обозрим. Как правило, он не
должен включать поля данных.
В идеале интерфейс должен быть полным (предоставлять
возможность выполнять любые разумные действия с
классом) и минимально необходимым (без дублирования
и пересечения возможностей методов).
61
Состав класса
Как правило, класс как тип, определенный пользователем,
должен содержать скрытые (private) поля и следующие
функциональные элементы:
конструкторы, определяющие, как инициализируются
объекты класса;
набор методов и свойств, реализующих характеристики
класса;
классы исключений, используемые для сообщений об
ошибках путем генерации исключительных ситуаций.
Классы, моделирующие математические или
физические понятия, обычно также содержат набор
операций, позволяющих копировать, присваивать,
сравнивать объекты и производить с ними другие
действия, требующиеся по сути класса.
62
Пример: опять снова счетчик
class Counter
{ int n; // скрытое поле класса
public class InvalidCounterValue : Exception { } // класс исключения
public Counter(int n) { // конструктор
if (n >= 0) this.n = n; else throw new InvalidCounterValue();
}
public override string ToString() // переопределение метода предка
{ return n.ToString(); }
public static Counter operator ++(Counter param) // ариф. операции
{ Counter temp = new Counter(param.n + 1); return temp; }
public static Counter operator +(Counter param, int delta)
{ Counter temp = new Counter(param.n + delta); return temp; }
public static Counter operator +(int delta, Counter param)
{ Counter temp = new Counter(param.n + delta); return temp; }
public static Counter operator +(Counter param1, Counter param2)
{ Counter temp = new Counter(param1.n + param2.n); return temp;
}
63
public static implicit operator int(Counter param) // операции
{ return param.n; } /* Counter в int */ // преобразования
public static implicit operator Counter(int n)
{ return new Counter(n); } /* int в Counter */
public int N // свойство
{ get { return n; }
set { if (value >= 0) n = value;
else throw new InvalidCounterValue(); }}
public void Inc(int delta){ n += Math.Abs(delta); } // методы
public void Inc() { ++n; }
}
class Program { static void Main(string[] args) // класс-клиент
{ try { Counter num = new Counter(); Counter num2 = new Counter(10);
num++; num2 += 5; int a = num + num2; int b = num;
num = 4; a = num + 3; num = b - a; Console.Write(num);
}
catch (Counter.InvalidCounterValue)
{ Console.WriteLine("Недопустимое значение счетчика"); }
catch { Console.WriteLine(«Какое-то исключение"); } } }
64
Элементы класса
Методы определяют поведение класса. Каждый метод
класса должен решать только одну задачу.
Создание любого метода следует начинать с его
интерфейса (заголовка). Необходимо четко представлять
себе, какие параметры метод должен получать и какие
результаты формировать. Входные параметры обычно
перечисляют в начале списка параметров.
Поля, характеризующие класс в целом, следует описывать
как статические.
Все литералы, связанные с классом, описываются как
поля-константы с именами, отражающими их смысл.
Необходимо стремиться к максимальному сокращению
области действия каждой переменной. Это упрощает
отладку программы, поскольку ограничивает область
поиска ошибки.
65
«Приведем аналогию с шахматами. Взрослому
человеку нужно 5 минут, чтобы запомнить
правила (как ходят/бьют пешки и фигуры,
правила рокировки и пр.), а для того чтобы стать
мастером игры, нужны сотни сыгранных партий.»
66