- •Лекция 2. С# и объектно-ориентированное программирование Формальное определение класса в с#
- •Ссылки на самого себя
- •Определение открытого интерфейса по умолчанию
- •Указание области видимости на уровне типа: открытые и внутренние типы
- •Столпы объектно-ориентированного программирования
- •Инкапсуляция
- •Наследование: отношения «быть» и «иметь»
- •Полиморфизм: классический и для конкретного случая
- •Средства инкапсуляции в с#
- •Реализация инкапсуляции при помощи традиционных методов доступа и изменения
- •Второй способ инкапсуляции: применение свойств класса
- •Поддержка наследования в с#
- •Применение модели включения-делегирования
- •Поддержка полиморфизма в с#
- •Абстрактные классы
- •Принудительный полиморфизм: абстрактные методы
- •Приведение типов в с#
- •Приведение числовых типов
- •Обработка исключений
- •Жизненный цикл объектов
- •Завершение ссылки на объект
- •Интерфейс iDisposable
- •Взаимодействие со сборщиком мусора
Реализация инкапсуляции при помощи традиционных методов доступа и изменения
Давайте вновь обратимся к нашему классу Employee. Если мы хотим, чтобы внешний мир смог работать с внутренними данными этого класса (пусть это будет только одна переменная fullName, для которой используется тип данных string), то традиционный подход рекомендует создание метода доступа (accessor, или get method) и метода изменения (mutator, или set method). Набор таких методов может выглядеть следующим образом:
// Определение традицонных методов доступа и изменения для закрытой переменной public class Employee
{
private string fullName:
. . . .
// Метод доступа
public string GetFullName( ) {return fullName; }
// Метод изменения
public void SetFullName(string n)
{
// Логика для удаления неположенных символов (!. @. #. $. % и прочих
// Логика для проверки максимальной длины и прочего
fullName = n;
}
}
Такой подход требует наличия двух методов для взаимодействия с каждой из переменных. Вызов этих методов может выглядеть следующим образом:
// Применение методов доступа и изменения
public static int Main(string[ ] args)
{
Employee p = new Employee();
p.SetFullName(“Fred");
Console.WriteLine(“Employee is named: " + p.GetFullName());
}
Второй способ инкапсуляции: применение свойств класса
Помимо традиционных методов доступа и изменения для обращения к закрытым членам класса можно также использовать свойства (properties). В Visual Basic и СОМ свойства — это привычный инструмент. Свойства позволяют имитировать доступ к внутренним данным класса: при получении информации или внесении изменений через свойство синтаксис выглядит так же, как при обращении к обычной открытой переменной. Но на самом деле любое свойство состоит из двух скрытых внутренних методов. Преимущество свойств заключается в том, что вместо того, чтобы использовать два отдельных метода, пользователь класса может использовать единственное свойство, работая с ним так же, как и с открытой переменной-членом данного класса:
// Обращ ение к имени сотрудника через свойство
public static int Main(string[ ] args)
{
Employee p = new Employee();.
// Устанавливаем значение
p.EmpID = 81;
// Получаем значение
Console.WriteLine(“Person ID is : {0}", p.EmpID);
return 0;
}
Если заглянуть внутрь определения класса, то свойства всегда отображаются в «реальные» методы доступа и изменения. А уже в определении этих методов вы можете реализовать любую логику (например, для устранения лишних символов, проверки допустимости вводимых числовых значений и прочего). Ниже представлен синтаксис класса Employee с определением свойства EmpID:
// Пользовательское свойство EmpID для доступа к переменной empIDi
public class Employee
{
private int empID;
// Свойство для empID
public int EmpID
{
get
{
return empID;
}
set
{
// Здесь вы можете реализовать логику для проверки вводимых
// значений и выполнения других действий
empID = value;
}
}
}
Свойство С# состоит из двух блоков — блока доступа (get block) и блока изменения (set block). Ключевое слово value представляет правую часть выражения при присвоении значения посредством свойства. Как и все в С#, то, что представлено словом value — это также объект. Совместимость того, что передается свойству как value, с самим свойством, зависит от определения свойства. Например, свойство EmpID предназначено (согласно своему определению в классе) для работы с закрытым целочисленным empID, поэтому число 81 вполне его устроит:
Необходимо отметить, что обращаться к объекту value можно только в пределах программного блока set внутри определения свойства. Попытка обратиться к этому объекту из любого другого места приведет к ошибке компилятора.
Последнее, что мы отметим — использование свойств (по сравнению с традиционными методами доступа и изменения) делает применение ваших типов более простым. Например, предположим, что в классе Employee предусмотрена внутренняя закрытая переменная для хранения информации о возрасте сотрудника. Вы чотите, чтобы при наступлении дня рождения этого сотрудника значение этой пе-оеменной увеличивалось на единицу. При использовании традиционных методов доступа и изменения эта операция будет выглядеть так:
Employee joe = new Employee();
Joe.SetAge(joe.GetAge() + 1);
Используя свойство, вы можете сделать это проще:
Employee joe = new Employee()ж
joe.Age++;