Лекция-06_Пр-е.ppt
- Количество слайдов: 27
http: //msdn. microsoft. com/ru-ru/library/67 ef 8 sbd. aspx Лекция 09. 10. 12 г. 1
Наследование Лекция 09. 10. 12 г. 2
Наследование – это аспект ООП, облегчающий повторное использование кода. Строго говоря, повторное использование кода существует в двух видах: Ø классическое наследование (отношение "является") и Ø модель включения/делегации (отношение "имеет"). Лекция 09. 10. 12 г. 3
1. Классическое наследование При установке между классами отношения "является" строится зависимость между двумя или более типами классов. Базовая идея, лежащая в основе классического наследования, заключается в том, что новые классы могут использовать (а также, возможно, расширять и/или видоизменять) функциональность существующих классов. Создадим класс Car: class Car { public readonly int max. Speed; private int curr. Speed; public Car(int max){ max. Speed = max; } public Car(){ max. Speed =55; } public int Speed { get { return curr. Speed; } set { curr. Speed += value; if (curr. Speed > max. Speed) curr. Speed = max. Speed; } } } Лекция 09. 10. 12 г. 4
Классическое наследование Тип Саr можно использовать следующим образом: //Ex 035. cs using System; class Program { static void Main(string[] args) { Console. Write. Line("***** Basic Inheritance *****n"); // Создать экземпляр типа Car my. Car = new Car(80); my. Car. Speed = 50; Console. Write. Line("My car is going {0} КMPH", my. Car. Speed); Console. Read. Line(); } } class Car { public readonly int max. Speed; private int curr. Speed; public Car(int max){ max. Speed = max; } public Car(){ max. Speed =55; } public int Speed { get { return curr. Speed; } set { curr. Speed += value; if (curr. Speed > max. Speed) curr. Speed = max. Speed; } } } Лекция 09. 10. 12 г. 5
Расширение родительского класса Предположим, что необходимо иметь еще один класс, содержащий все поля класса Car, а также дополнительные поля, свойственные именно данному классу. Назовем такой класс Mini. Van. Классы Саr и Mini. Van будут взаимосвязаны; фактически можно сказать, что каждый объект класса Mini. Van одновременно является и объектом класса Саr. Существующий класс, который будет служить основой для нового класса, называется базовым или родительским классом. Класс, расширяющий базовый класс, называется производным, или дочерним классом. В С# для установки между классами отношения "является" используется операция двоеточия в определении класса: // Mini. Van “является” Саr class Mini. Van : Car { } Лекция 09. 10. 12 г. 6
Расширение родительского класса Учитывая отношение между этими классами, можно использовать Mini. Van следующим образом: //Ex 036. cs using System; class Program { static void Main(string[] args) { Console. Write. Line("***** Basic Inheritance *****n"); // Создать экземпляр типа Mini. Van. Доступ к свойству, Mini. Van my. Van = new Mini. Van(); определенному в классе Car my. Van. Speed = 10; Console. Write. Line("My van is going {0} KMPH", my. Van. Speed); Console. Read. Line(); } } class Car { public readonly int max. Speed; private int curr. Speed; public Car(int max){ max. Speed = max; } public Car(){ max. Speed =55; } public int Speed { get { return curr. Speed; } set { curr. Speed += value; if (curr. Speed > max. Speed) curr. Speed = max. Speed; } } } // Mini. Van "является" Саr class Mini. Van : Car { } Лекция 09. 10. 12 г. 7
Расширение родительского класса Доступ к приватным членам базового класса из методов производного класса закрыт: //Ex 036. cs using System; class Program { static void Main(string[] args) { Console. Write. Line("***** Basic Inheritance *****n"); // Создать экземпляр типа Mini. Van my. Van = new Mini. Van(); my. Van. Speed = 10; Console. Write. Line("My van is going {0} KMPH", my. Van. Speed); Console. Read. Line(); } } class Car { public readonly int max. Speed; private int curr. Speed; public Car(int max){ max. Speed = max; } public Car(){ max. Speed =55; } public int Speed { get { return curr. Speed; } set { curr. Speed += value; if (curr. Speed > max. Speed) curr. Speed = max. Speed; } } } // Mini. Van "является" Саr class Mini. Van : Car { public void Test. Method() { Speed =10; Доступ к приватному свойству, curr. Speed =10; определенному в классе Car, вызовет } Лекция 09. 10. 12 г. ошибку на этапе компиляции!!!!!! } 8
О множественном наследовании Некоторые языки программирования (например, С++) разрешают множественное наследование, позволяя создать класс, наследующий двум или более базовым классам. Однако CLR, а значит, и все основанные на ней языки программирования, не поддерживают множественное наследование. Вместе с тем, CLR позволяет реализовать ограниченное множественное наследование через интерфейсы: Ø классы поддерживают одиночное наследование реализации и множественное наследование интерфейсов; Ø структуры не поддерживают наследование реализации и поддерживают множественное наследование интерфейсов. Лекция 09. 10. 12 г. 9
Пример с наследованием Sales. Person "является" Employee (как и Manager) Лекция 09. 10. 12 г. 10
Пример с наследованием В модели классического наследования базовые классы (вроде Employee) используются для определения характеристик, общих для всех наследников. Подклассы (такие как Sales. Person и Manager) расширяют общую функциональность, добавляя дополнительное специфическое поведение. Для нашего примера предположим, что класс Manager расширяет Employee, храня количество опционов на акции, в то время как класс Sales. Person поддерживает количество продаж. Код данных классов выглядит следующим образом: Лекция 09. 10. 12 г. 11
Базовый класс Employee //Employee. cs using System; using System. Collections. Generic; using System. Text; namespace Employees { abstract class Employee { // Вложенный тип public class Benefit. Package { public enum Benefit. Package. Level { Standard, Gold, Platinum } public double Compute. Pay. Deduction() { return 125. 0; } } // Поля private static string company. Name; private float curr. Pay; private int emp. Age; protected Benefit. Package emp. Benefits = new Benefit. Package(); private int emp. ID; private string emp. Name; private string emp. SSN; … Лекция 09. 10. 12 г. 12
Базовый класс Employee … // Свойства public int Age { get { return emp. Age; } set { emp. Age = value; } } public Benefit. Package Benefits { get { return emp. Benefits; } set { emp. Benefits = value; } } public static string Company { get { return company. Name; } set { company. Name = value; } } public int ID { get { return emp. ID; } set { emp. ID = value; } } public string Name { get { return emp. Name; } set { emp. Name = value; } } public float Pay { get { return curr. Pay; } set { curr. Pay = value; } } public string Social. Security. Number { get { return emp. SSN; } } … Лекция 09. 10. 12 г. 13
Базовый класс Employee … // Методы public virtual void Display. Stats() { Console. Write. Line("Name: {0}", emp. Name); Console. Write. Line("ID: {0}", emp. ID); Console. Write. Line("Age: {0}", emp. Age); Console. Write. Line("SSN: {0}", emp. SSN); Console. Write. Line("Pay: {0}", curr. Pay); } public Employee() { } public Employee(string name, int age, int id, float pay, string ssn) { emp. Name = name; emp. ID = id; emp. Age = age; curr. Pay = pay; emp. SSN = ssn; } static Employee() { company. Name = "Intertech Training"; } public double Get. Benefit. Cost() { return emp. Benefits. Compute. Pay. Deduction(); } public virtual void Give. Bonus(float amount) { curr. Pay += amount; } } } Лекция 09. 10. 12 г. 14
Производный класс Manager //Manager. cs using System; using System. Collections. Generic; using System. Text; namespace Employees { class Manager : Employee { // Поля private int number. Of. Options; // Свойства public int Stock. Options { get { return number. Of. Options; } set { number. Of. Options = value; } } // Методы public Manager() { } public Manager(string full. Name, int age, int emp. ID, float curr. Pay, string ssn, int numb. Of. Opts) : base(full. Name, age, emp. ID, curr. Pay, ssn) { number. Of. Options = numb. Of. Opts; } public override void Display. Stats() { base. Display. Stats(); Console. Write. Line("Number of Stock Options: {0}", number. Of. Options); } public override void Give. Bonus(float amount) { base. Give. Bonus(amount); Random r = new Random(); number. Of. Options += r. Next(500); } } } Лекция 09. 10. 12 г. 15
Производный класс Sales. Person //Sales. Person. cs using System; using System. Collections. Generic; using System. Text; namespace Employees { class Sales. Person : Employee { private int number. Of. Sales; public int Sales. Number { get { return number. Of. Sales; } set { number. Of. Sales = value; } } public Sales. Person() { } public Sales. Person(string full. Name, int age, int emp. ID, float curr. Pay, string ssn, int numb. Of. Sales) : base(full. Name, age, emp. ID, curr. Pay, ssn) { number. Of. Sales = numb. Of. Sales; } public override sealed void Give. Bonus(float amount) { int sales. Bonus = 0; if (number. Of. Sales >= 0 && number. Of. Sales <= 100) { sales. Bonus = 10; } else { if (number. Of. Sales >= 101 && number. Of. Sales <= 200) { sales. Bonus = 15; } else { sales. Bonus = 20; } } base. Give. Bonus(amount * sales. Bonus); } public override void Display. Stats() { base. Display. Stats(); Console. Write. Line("Number of Sales: {0}", number. Of. Sales); } } } Лекция 09. 10. 12 г. 16
Пример с наследованием Классы Sales. Person и Manager автоматически наследуют все общедоступные члены базового класса Employee. Для иллюстрации запишем метод Main() следующим образом: //Program. cs using System; using System. Collections. Generic; using System. Text; namespace Employees { class Program { static void Main(string[] args) { Console. Write. Line("***** The Employee Class Hierarchy *****n"); Sales. Person danny = new Sales. Person(); danny. Age = 31; danny. Name = "Danny"; danny. Sales. Number = 50; danny. Display. Stats(); Console. Read. Line(); } } } Лекция 09. 10. 12 г. 17
Управление созданием базового класса с помощью ключевого слова base Пусть необходимо реализовать конструктор для класса Manager с шестью параметрами. Это можно сделать так: public Manager(string full. Name, int age, int emp. ID, float curr. Pay, string ssn, int numb. Of. Opts) { // Это поле определено классом Manager. number. Of. Options = numb. Of. Opts; // Присвоим входные параметры, используя // наследованные свойства родительского класса. ID = emp. ID; Age = age; Name = full. Name; Pay = curr. Pay; Ошибка !!! Social. Security. Number = ssn; Свойство Social. Security. Number в } родительском классе определено как доступное только для чтения, поэтому мы не можем присвоить значение входного параметра string этому свойству Лекция 09. 10. 12 г. 18
Управление созданием базового класса с помощью ключевого слова base В предыдущем примере мы неявно создали довольно неэффективный конструктор, учитывая тот факт, что в С#, если не указать иного, конструктор базового класса вызывается автоматически перед выполнением логики производного конструктора. После этого момента текущая реализация имеет доступ к многочисленным общедоступным свойствам базового класса Employee для установки его состояния. Таким образом, в действительности во время создания объекта Manager выполняется семь действий (обращений к пяти унаследованным свойствам и двум конструкторам). Можно сократить количество вызовов инициализаций унаследованных членов (что сэкономит время процессора). Модифицируем специальный конструктор типа Manager, применив ключевое слово base: public Manager(string full. Name, int age, int emp. ID, float curr. Pay, string ssn, int numb. Of. Opts) : base(full. Name, age, emp. ID, curr. Pay, ssn) { number. Of. Options = numb. Of. Opts; } В данной ситуации явно вызывается конструктор, базового класса Employee, что избавляет от излишних вызовов Лекция 09. 10. 12 г. 19
Управление созданием базового класса с помощью ключевого слова base Специальный конструктор Sales. Person выглядит почти идентично: public Sales. Person(string full. Name, int age, int emp. ID, float curr. Pay, string ssn, int numb. Of. Sales) : base(full. Name, age, emp. ID, curr. Pay, ssn) { number. Of. Sales = numb. Of. Sales; } Ключевое слово base можно использовать в любой момент, когда подкласс желает обратиться к общедоступному или защищенному члену, определенному в родительском классе. Применение этого ключевого слова не ограничено логикой конструктора (т. е. его можно использовать и в других методах класса) Лекция 09. 10. 12 г. 20
Ключевое слово protected Язык программирования С#, как и многие другие современные объектные языки, предлагает дополнительное ключевое слово protected (защищенный) для определения доступности членов. Когда базовый класс определяет защищенные данные или защищенные члены, он устанавливает набор элементов, которые могут быть доступны непосредственно любому наследнику. Если мы хотим позволить дочерним классам Sales. Person и Manager непосредственно обращаться к блоку данных, определенному Employee, можете изменить исходный класс Employee следующим образом: class Employee { // Теперь производные классы могут напрямую обращаться к этой информации. protected string emp. Name; protected int emp. ID; protected float curr. Pay; protected int emp. Age; protected string emp. SSN; protected static string company. Name; . . . } Лекция 09. 10. 12 г. 21
Ключевое слово protected Преимущество определения защищенных членов в базовом классе состоит в том, что производным типам больше не нужно обращаться к данным опосредованно, используя общедоступные методы и свойства. Возможным минусом такого подхода, конечно же, является то, что когда производный тип имеет прямой доступ к внутренним данным его родителя, возникает вероятность нечаянного нарушения существующих бизнес-правил, содержащихся в общедоступных свойствах. В то же время, с точки зрения пользователя объекта производного типа защищенные данные трактуются как приватные (поскольку пользователь находится "вне" семейства). Лекция 09. 10. 12 г. 22
2. Модель включения/делегации Мы рассмотрели отношение типа «является» . Рассмотрим теперь отношение «имеет» . Предположим, что создан новый класс, который моделирует пакет льгот для сотрудников: public class Benefit. Package { public enum Benefit. Package. Level { Standard, Gold, Platinum } public double Compute. Pay. Deduction() { return 125. 0; } } По логике следует, что каждый сотрудник должен иметь пакет льгот (т. е. Employee «имеет» Benefit. Package). Для реализации отношения «имеет» код нужно записать так: class Employee {. . . protected Benefit. Package emp. Benefits = new Benefit. Package(); . . . } Лекция 09. 10. 12 г. 23
Модель включения/делегации Делегация – это просто прием добавления членов к включающему классу, которые используют функциональность включенного объекта. Например, в коде класса Employee имеется свойство Benefit, представляющее включенный объект emp. Benefits, а также метод по имени Get. Benefit. Cost(), использующий функциональность вложенного класса Benefit. Package: class Employee {. . . public double Get. Benefit. Cost() { return emp. Benefits. Compute. Pay. Deduction(); } public Benefit. Package Benefits { get { return emp. Benefits; } set { emp. Benefits = value; } } } Лекция 09. 10. 12 г. 24
Модель включения/делегации У классов, унаследованных от Employee, также станет доступным метод Get. Benefit. Cost(): static void Main(string[] args) { Console. Write. Line("***** The Employee Class Hierarchy *****n"); Manager chucky = new Manager("chucky", 50, 92, 100000, "333 -23 -2322", 9000); double cost = chucky. Get. Benefit. Cost() ; Console. Read. Line(); } Лекция 09. 10. 12 г. 25
Вложенные типы В С# (как и в других языках. Net) допускается определять тип (перечисление, класс, интерфейс, структуру или делегат) непосредственно внутри контекста класса или структуры. При этом вложенный (или "внутренний") тип считается членом охватывающего (или "внешнего") класса, и им можно манипулировать как любым другим членом класса (полем, свойством, методом, событием и т. п. ). Синтаксис, используемый для вложения типа, достаточно прост: public class Outer. Class { // Общедоступный вложенный тип может быть использован повсюду. public class Public. Inner. Class { } // Приватный вложенный тип может быть использован // только членами включающего класса. private class Privatelnner. Class { } } Вложенные типы позволяют получить четкий контроль над уровнем доступа внутреннего типа, поскольку они могут быть объявлены как приватные (напомним, что невложенные классы не могут быть объявлены с использованием ключевого слова private). Поскольку вложенный тип является членом включающего класса, он может иметь доступ к приватным членам включающего класса. Часто вложенные типы удобны в качестве вспомогательных для внешнего класса и не предназначены для использования внешним миром. Лекция 09. 10. 12 г. 26
Модель включения/делегации Если необходимо использовать вложенный тип извне включающего типа, то должны квалифицировать его именем включающего типа. Рассмотрим следующий код: static void Main(string[] args) { // Создать и использовать общедоступный вложенный класс. ОК! Outer. Class. Public. Inner. Class inner; inner = new Outer. Class. Public. Inner. Class(); // Ошибка компилятора! Нет доступа к приватному классу. Outer. Class. Privatelnner. Class inner 2; inner 2 = new Outer. Class. Privatelnner. Class(); } Лекция 09. 10. 12 г. 27
Лекция-06_Пр-е.ppt