
- •Лекции по курсу "Технология программирования" (1-й семестр) Оглавление
- •Технология .Net Предыдущее состояние дел.
- •Главные компоненты платформы .Net (clr, cts и cls)
- •Общеязыковая среда выполнения (clr)
- •О бщая система типов и общеязыковые спецификации (cts и cls)
- •Библиотека базовых классов
- •Роль языка с#
- •Компоновочные блоки
- •Роль метаданных типов .Net
- •Роль манифеста компоновочного блока
- •Общая система типов.
- •Объектно-ориентированное программирование
- •Главные элементы объектно-ориентированного подхода
- •Дополнительные элементы ооп
- •Принципы объектно-ориентированного программирования.
- •Классы Инкапсуляция
- •Объект (экземпляр класса).
- •Ключевое слово this
- •Отношения между объектами.
- •Основные отличительные особенности класса
- •Спецификаторы доступа
- •Состав класса
- •Поля класса
- •Доступ к полям
- •Статические и экземплярные переменные
- •Методы (функции-члены класса)
- •Переменное число параметров метода
- •Статические методы
- •Конструкторы
- •Закрытые конструкторы или классы без экземпляров
- •Статические конструкторы.
- •Деструкторы
- •Абстрактные методы и классы.
- •Свойства
- •Индексаторы
- •Статические классы
- •Частичные классы
- •Рекомендации по программированию
- •Наследование Понятие наследования в программировании
- •Типы наследования
- •Наследование реализации
- •Определение наследующих классов
- •Уровень доступа protected и internal
- •Ссылка на объект базового класса
- •Протоклассы
- •Предотвращение наследования с помощью ключевого слова sealed.
- •Отношения между классами
- •Абстрактные классы.
- •Класс object
- •Функциональные замыкания
- •Разработка функциональных замыканий с помощью наследования
- •Разработка функциональных замыканий с помощью экземпляров класса
- •Заключение.
- •Полиморфизм
- •Полиморфизм наследующих классов.
- •Переопределение методов родительского класса. Раннее связывание.
- •Виртуальные методы и их переопределение.
- •Как вызывают виртуальные методы
- •Виртуальные функции и принцип полиморфизма
- •Перегрузка.
- •Перегруженные конструкторы
- •Рекомендации программисту.
Уровень доступа protected и internal
Язык С# накладывает строгое ограничение на прямой доступ к закрытым элементам так, что даже производный класс, тесно связанный с базовым классом, не может получить доступа к закрытым (private) членам базового класса.
Методы производных классов, как правило, могут обращаться к членам базового класса, которые были объявлены с уровнем доступа public, protected и internal путем непосредственного использования их имен.
Использование уровня доступа protected (защищенный) предлагает промежуточный уровень защиты между уровнями доступа public и private. Доступ к protected членам базового класса может быть осуществлен только в этом базовом классе или в любых классах, производных от этого базового класса.
Другой промежуточный уровень доступа к членам класса называется internal (внутренний). Доступ к internal членам базового класса может осуществляться только объектами тех классов, которые объявлены в том же самом компоновочном блоке. То есть internal член класса доступен в любой части компоновочного блока, в котором объявлен этот internal член класса.
Для разрешения доступа производному классу к закрытым элементам базового класса можно объявить их в базовом классе как защищенные (protected). Доступ к защищенным членам базового класса возможен только в этом же базовом классе или в любых других классах, которые являются производными для этого базового класса. В следующем примере показана соответствующая модификация классов для объектов типа точки и окружности.
using System;
//Определение класса точки неявно наследует все из класса Object
public class Point2
{
// Данные:
protected int x, y; // Координаты точки
// Методы:
public Point2() // конструктор по умолчанию (без аргументов)
{
// здесь происходит неявное обращение к конструктору Object
x=100; y=100; // если явно не указать значения x и y, то они будут равны 0
}
public Point2(int a, int b) // конструктор
{
// здесь происходит неявное обращение к конструктору Object
x=a; y=b;
}
// Открытые функции доступа к закрытым данным
public void SetX(int a){ x=a; }
public int GetX(){ return x; }
public void SetY(int a){ y=a; }
public int GetY(){ return y; }
// Функции общего назначения
public void Move(int dx, int dy) { x+=dx; y+=dy; }
}
class Circle4: Point2
{
// Данные:
private double radius; // радиус окружности
// Методы:
public Circle4()// конструктор по умолчанию (без аргументов)
{
// здесь происходит неявное обращение к конструктору Object
// если явно не указать значения x и у, то они будут равны 0
x=100; y=100; // Ошибки уже нет!
radius=10;
}
public Circle4(int a, int b, double r) // конструктор
{
// здесь имеет место неявное обращение к конструктору класса Point (x=y=0)
x=a; y=b; // Ошибки уже нет!
radius=r;
}
public int GetRad(){ return radius; }
public void SetRad(double r){ if(r>0) radius=r; }
public double Area(){ return Math.Pi*Math.Pow(radius,2); }
}
Использование защищенных переменных в базовом классе создает две проблемы. В-первых, объекту производного класса уже можно будет не использовать открытую функцию доступа к закрытой переменной базового класса. Следовательно, объект производного класса мог бы легко присвоить недопустимое значение унаследованным защищенным данным. Второй проблемой использования защищенных данных является то, что методы производного класса, вероятнее всего, будут написаны в зависимости от реализации базового класса. На практике же, производные классы должны зависеть только от возможностей, предоставляемых базовым классом (т.е. от методов, не принадлежащих приватному уровню доступа), а не от реализации членов этого базового класса. И напротив, объявление переменных базового класса приватными позволяет программистам изменять реализацию базового класса без необходимости изменения реализации производного класса. Поэтому по возможности следует избегать включения защищенных данных в базовый класс. Вместо этого лучше включить не приватные методы, имеющие доступ к приватным данным, что позволит поддерживать согласованное состояние объектов. Примером такой реализации может служить ранее описанный нами класс Circle3.
Теперь рассмотрим еще один вариант построения класса окружности.
class Circle5: Point
{
// Данные:
private double radius; // радиус окружности
// Методы:
public Circle2()// конструктор по умолчанию (без аргументов)
{
// здесь происходит неявное обращение к конструктору Object
// в результате которого значения x и у станут равными 0
SetX(100); SetY(100); radius=10;
}
// конструктор с явным обращением к конструктору базового класса Point
public Circle2(int a, int b, double r) : base(x, y)
{
radius=r;
}
public double Area(){ return Math.Pi*Math.Pow(radius, 2); }
}
С целью демонстрации явного и неявного вызова конструктора базового класса здесь используется второй конструктор, который явно обращается к конструктору базового класса. Такое явное обращение к конструктору базового класса осуществляется с помощью применения синтаксиса вызова базового класса (т.е. ссылки base, за которой следует пара круглых скобок, содержащих аргументы конструктора базового класса). Символ двоеточия, за которым следует ключевое слово base, осуществляет явный доступ к соответствующей версии конструктора базового класса. Использование такого подхода является хорошим стилем программирования. Дело в том, что если метод объекта выполняет операции, необходимые другому объекту, то следует вызвать этот метод, а не копировать его тело. Дублирование кода создает проблемы его поддержки.
В свою очередь от класса окружности можно породить и другие классы, например, класс цилиндра.
class Cylinder: Circle5
{
// Данные:
private double height; // высота цилиндра
// Методы:
public Cylinder()// конструктор по умолчанию (без аргументов)
{
// здесь происходит неявное обращение к конструктору Circle
height=25;
}
// конструктор с явным обращением к конструктору базового класса Circle
public Cylinder(int a, int b, double r, double h) : base(x, y, r)
{
height=h;
}
public double GetVolume(){ return Area() * height; }
}