Синтаксис наследования
Рыбы Млекопитающие Птицы
Акула Карась Дельфин Слон Пингвин Петух
Пресмыкающиеся Насекомые
Динозавр Блоха
Это тривиальная классификация, а вернее её отсутствие, т. к. реальные классификации заглублённые. Это, фактически, напоминание о том, что иерархия существует и всякая наука начинается с систематизации знаний.
Синтаксис наследования:
При наследовании имена производного и базового классов разделяются двоеточием (:). Производный класс получает все поля, свойства и методы базового класса.
Class (Имя класса потомка): (Имя класса предка)
{
…
}
Пример: класс Bird (Птица) наследует класс Vertebrate (Позвоночное)
class Vertebrate // класс "Позвоночное животное"
{
public int NumberOfLegs;
public void Eat()
{
//код, заставляющий есть
}
}
class Bird : Vertebrate //расширяется класс путём добавления в конец его двоеточия и имени базового класса
{
public double Wingspan;
public void Fly()
{
//код, заставляющий летать
}
}
class Program
{//запускалка:
static void Main()
{
//так как tweety — это экземпляр объекта Bird, то он имеет методы и поля этого объекта
Bird tweety = new Bird();
tweety. Wingspan = 7.5; // tweety-название птички
tweety. Fly();
//т. к. класс Bird — производный по отношению к классу Vertebrate, все экземпляры Bird имеют поля и методы, определённые в классе Verteble
tweety. NumberOfLegs = 2;
tweety. Eat();
}
}
С точки зрения наследования все классы являются потомками класса cObject.
Таким образом, понятие наследования — это попытка определить понятие подтипа или подкласса. Всякого рода явные преобразования возможны, а средства явного преобразования типов есть. Грубо говоря, это схема «всё-во-всё». Реализация очевидна: всё рассматривается на уровне двоичных полей.
Открытие реализации для проектирования классов — опция protected
Для реализации ограничения доступа применяется модификатор доступа — PROTECTED.
—protected- виден классам-наследникам. Это даёт возможность скрыть от наследников переменную так, чтобы они не «напортачили» в ней.
-internal — модификатор доступа, который используется для методов, доступных всем классам, определённым в конкретной сборке
//Задача: Черепашья графика. Простая модель управления роботом-черепашкой, которая рисует на дискретной сетке.
using System;
using System. Collections. Generic;
using System. Linq;
using System. Text;
namespace Turtle//логически связанное определение имён
{
enum tColor {red, green, blue} //перечисляемый тип, состоящий из цветов
class cCell
{
protected internal tColor Color; // определение переменной Color типа tColor
//set-get:
public tColor GetColor() {return Color;}
cCell (tColor Color) {this. Color= Color;} // конструктор
} //class cCell
class cGrid //сетка
{
protected int Dim; //размерность
public int GetDim() {return Dim;}
protected internal cCell [,] Grid; //сама сетка(массив Grid) типа cCell (т. е. перечисляемого типа {red, green, blue})
// текущая позиция
protected internal int CurrentX; //, где internal означает то, что доступ ограничен текущей сборкой,
//а protected означает, что доступ ограничен содержащим классом или типами, которые являются производными от содержащего класса.
protected internal int CurrentY;
public int GetCurrentX() {return CurrentX; }
public int GetCurrentY() {return CurrentY; }
protected void Move (int x, int y )
{
//модульная арифметика(выход за границы)
CurrentX+=x; //эквивалентно выражению CurrentX = CurrentX + x
CurrentY+=y;
} // Move
cGrid (int Dim, tColor Color) //конструктор
{
this. Dim= Dim;
CurrentX=0;
CurrentY=0;
Grid= new cCell [Dim, Dim];
for (int i= 0; i<Dim; i++)
for (int j=0; j<Dim; j++)
Grid [i, j].Color= Color;
}
}
class TurtleGraphics: cGrid
{
//поля
public bool PenDown;// состояние пера — карандаш
public tColor PenColor;
public void GoUp()
{
Move(0,1);
if (PenDown) Grid[CurrentX, CurrentY].Color = PenColor;
}//PenGoUp
public void GoRight()
{
Move(1,0);
if (PenDown) Grid[CurrentX, CurrentY]. Color = PenColor;
}//PenRight
public void GoLeft()
{
Move(-1,0);
if (PenDown) Grid[CurrentX, CurrentY]. Color = PenColor;
}//PenLeft
public void GoDown ()
{
Move(0,-1);
if (PenDown) Grid[CurrentX, CurrentY]. Color = PenColor;
}//PenDown
}//class TurtleGraphics: cGrid
class Program
{ //запускалка
static void Main ()
{
}
}//class Program
}//namespace TurtleGraphics
Итак, появляются отношения использования и отношения наследования.
Полиморфизм
Полиморфизм как уточнение семантики типа переменной
Полиморфизм (многообразие) — возможность уточнения (изменения) не только значений, состояний объекта, но и поведения объекта, то есть связанных с ним функций изменения состояния или алгоритмов реализации (изменение (уточнение) фактического типа переменной в ходе выполнения программы).
Описание типа переменной теперь означает лишь присвоение начального типа по умолчанию.
Тип
Строгая типизация – алгоритмика (типы минимизируют ошибки алгоритмов). Поэтому тип переменной описывается до появления переменной. Строгая типизация ориентирована на создание надёжных программ.
Бестиповая логика — свободная типизация (например, языки низкого уровня (ассемблер)). Свободная типизация ближе к системному программированию (ближе к эффективным реализациям)
Полиморфизм радикально изменяет взгляд на понятие типа. Если раньше переменная принадлежала одному фиксированному типу, то теперь она изменяет его, то есть тип становится атрибутом переменной.
Полиморфное программирование – сложное программирование ещё и потому, что не во всём адекватно поддерживается реализация.
Пример:
Пусть Dad – объект класса cDad,
Son – выражение класса cSon, где cDad – предок cSon в отношении наследования.
1) Присваивание Son:=Dad — синтаксическая ошибка.
Допустимо явное присваивание типов переменной любого класса любому другому классу:
Son:=Dad as cSon; — не является синтаксической ошибкой, но обращение к полям и методам сына, отсутствующим у отца, будет ошибкой времени выполнения.
2) В любом случае допустимо присваивание:
Dad:=Son и, соответственно, употребление выражений типа cSon вместо значений типа cDad, например, при обращении к процедуре.
Замечание:
Как всегда происходит присваивание ссылок. Это означает, что ссылка на предыдущие значения, связанная со ссылкой Dad, будет потеряна. Переменная Dad изменяет свой тип с формально описанного статического типа cDad на фактический тип cSon.
Что это означает для значений? Преображает ли Dad новые поля?
Фактически – да, формально – нет. Для таких полей обращение Dad. p неверно, а верно (Dad as cSon).p, где оператор as используется для выполнения определенных типов преобразований между совместимыми ссылочными типами.
Точно так же дело обстоит с методами, которые есть у сына, но нет у отца.
Изменяются ли значения, то есть реализация одноимённых функций?
1) Да, если таковые при объявлении класса cDad объявлены как виртуальные (virtual) или динамические (dynamic) (одна семантика, разная реализация), а при определении класса cSon – как переписывающие метод предка (override).