Управлять большим количеством разрозненных классов довольно сложно. С этой проблемой можно справиться путем упорядочивания и ранжирования классов, то есть объединяя общие для нескольких классов свойства в одном классе и используя его в качестве базового. Эту возможность предоставляет механизм наследования.
Наследование применяется для следующих взаимосвязанных целей:
Наследование является единственной возможностью использовать объекты, исходный код которых недоступен, но в которые требуется внести изменения.
Кроме механизма наследования в данном разделе мы рассмотрим такие важные понятия ООП как полиморфизм и инкапсуляцию (см. лекцию 2), которые также принимают участие в формировании иерархии классов.
Класс в С# может иметь произвольное количество потомков и только одного предка. При описании класса имя его предка записывается в заголовке класса после двоеточия. Если имя предка не указано, предком считается базовый класс всей иерархии System.Object. Синтаксис наследования:
[атрибуты] [спецификаторы] class имя_класса [: предки]
{ тело_класса}
Обратите внимание на то, что слово "предки" присутствует в описании класса во множественном числе, хотя класс может иметь только одного предка. Это связано с тем, что класс наряду с единственным предком-классом может наследовать интерфейсы (специальный вид классов, не имеющих реализации). Интерфейсы будут рассмотрены чуть позже.
Класс, который наследуется, называется базовым. Класс, который наследует базовый класс, называется производным. Производный класс, наследует все переменные, методы, свойства, операторы и индексаторы, определенные в базовом классе, кроме того в производный класс могут быть добавлены уникальные элементы или переопределены существующие.
Рассмотрим наследование классов на примере геометрических фигур на плоскости. В качестве базового класса создадим класс DemoPoint (точка на плоскости), в качестве производного класса от DemoPoint класс DemoLine (отрезок на плоскости):
class DemoPoint //базовый класс
{
public int x;
public int y;
public void Show()
{
Console.WriteLine("({0}, {1})", x, y);
}
}
class DemoLine : DemoPoint //производный класс
{
public int xEnd;
public int yEnd;
public void Show()
{
Console.WriteLine("({0}, {1})-({2}, {3})", x, y ,xEnd, yEnd);
}
}
class Program
{
static void Main()
{
DemoPoint point = new DemoPoint();
point.x = 0;
point.y = 0;
point.Show();
DemoLine line = new DemoLine();
line.x = 2; line.y = 2;
line.xEnd = 10; line.yEnd = 10;
line.Show();
}
}
Экземпляр класса DemoLine с одинаковой легкостью использует как собственные поля и методы, так и унаследованные от класса DemoPoint. При этом, если метод производного класса называется также как и метод базового класса, то вызывается метод производного. Однако компилятором будет сгенерировано предупреждение:

Чтобы избежать подобного предупреждения необходимо перед одноименным членом производного класса, в данном случае перед методом Show в классе DemoLine, поставить спецификатор new. Данный спецификатор скрывает одноименный член базового класса и предупреждений выдаваться не будет.
В нашем примере поля x и у базового класса были открыты для доступа ( public ). Если убрать public, то поля автоматически станут закрытыми для доступа ( private ), в том числе и для доступа из производного класса. Решить проблему доступа к закрытым полям базового класса из производного можно двумя способами: используя свойства класса или спецификатор protected. При объявлении какого-то члена класса с помощью спецификатора protected, он становится закрытым для всех классов, кроме производных.
class DemoPoint
{
protected int x;
protected int y;
public void Show()
{
Console.WriteLine("({0}, {1})",x, y);
}
}
class DemoLine : DemoPoint
{
public int xEnd;
public int yEnd;
public new void Show()
{
x=2; y=2; //доступ к закрытым полям базового класса
Console.WriteLine("({0}, {1})-({2}, {3})", x, y, xEnd, yEnd);
}
}
class Program
{
static void Main()
{
DemoPoint point = new DemoPoint();
point.Show();
DemoLine line = new DemoLine();
//line.x = 2; line.y = 2; //доступ к полям закрыт
line.xEnd = 10; line.yEnd = 10;
line.Show();
}
}
}
Обратите внимание на то, что доступ к полям х и y из класса Program невозможен, а из производного класса DemoLine возможен.
В иерархии классов как базовые, так и производные классы могут иметь собственные конструкторы. При этом конструктор базового класса создает часть объекта, соответствующую базовому классу, а конструктор производного класса - часть объекта, соответствующую производному классу. Так как базовый класс не имеет доступа к элементам производного класса, то их конструкторы должны быть раздельными.
В предыдущем примере классы создавались за счет автоматического вызова средствами С# конструктора по умолчанию. Добавим конструктор только в производный класс DemoLine:
class DemoPoint
{
protected int x;
protected int y;
public void Show()
{
Console.WriteLine("({0}, {1})",x, y);
}
}
class DemoLine : DemoPoint
{
public int xEnd;
public int yEnd;
new public void Show()
{
Console.WriteLine("({0}, {1})-({2}, {3})", x, y, xEnd, yEnd);
}
public DemoLine(int x1, int y1, int x2, int y2) //конструктор производного класса
{
x = x1; y = y1;
xEnd = x2; yEnd = y2;
}
}
class Program
{
static void Main()
{
DemoPoint point = new DemoPoint(); //вызывается конструктор по умолчанию
point.Show();
DemoLine line = new DemoLine(2, 2, 10, 10); //вызывается собственный конструктор
line.Show();
}
}
В данном случае конструктор определяется только в производном классе, поэтому часть объекта, соответствующая базовому классу, создается автоматически с помощью конструктора по умолчанию, а часть объекта, соответствующая производному классу, создается собственным конструктором.
Если же конструкторы определены и в базовом, и в производном классе, то процесс создания объектов несколько усложняется, т.к. должны выполниться конструкторы обоих классов. В этом случае используется ключевое слово base, которое имеет два назначения:
Производный класс может вызывать конструктор, определенный в его базовом классе, используя расширенную форму объявления конструктора и ключевое слово base. Формат расширенного объявления:
конструктор_производного_класса (список_параметров) : base (список_аргументов)
{ тело конструктора }
где с помощью элемента списка аргументов передаются параметры конструктору базового класса. Например:
class DemoPoint
{
protected int x;
protected int y;
public void Show()
{
Console.WriteLine("({0}, {1})",x, y);
}
public DemoPoint (int x, int y)//конструктор базового класса
{
this.x=x; this.y=y;
}
}
class DemoLine : DemoPoint
{
public int xEnd;
public int yEnd;
new public void Show()
{
Console.WriteLine("({0}, {1})-({2}, {3})", x, y, xEnd, yEnd);
}
public DemoLine(int x1, int y1, int x2, int y2):base(x1, y1) //конструктор производного класса
{
xEnd = x2; yEnd = y2;
}
}
class Program
{
static void Main()
{
DemoPoint point= new DemoPoint(5, 5);
point.Show();
DemoLine line = new DemoLine( 2, 2, 10, 10);
line.Show();
}
}
this, а в конструкторе производного класса нет.В общем случае с помощью ключевого слова base можно вызвать конструктор любой формы, определенный в базовом классе. Реально же выполнится тот конструктор, параметры которого будут соответствовать переданным при вызове аргументам. Например:
class DemoPoint
{
protected int x;
protected int y;
public void Show()
{
Console.WriteLine("({0}, {1})",x, y);
}
public DemoPoint () //конструктор базового класса по умолчанию
{
this.x=1; this.y=1;
}
public DemoPoint (int x, int y) //конструктор базового класса с параметрами
{
this.x=x; this.y=y;
}
}
class DemoLine : DemoPoint
{
public int xEnd;
public int yEnd;
new public void Show()
{
Console.WriteLine("({0}, {1})-({2}, {3})", x, y, xEnd, yEnd);
}
public DemoLine() //конструктор производного класса по умолчанию
{
xEnd = 100; yEnd = 100;
}
public DemoLine(int x2, int y2) //конструктор производного класса с двумя параметрами
{
xEnd = x2; yEnd = y2;
}
//конструктор производного класса с четырьмя параметрами
public DemoLine(int x1, int y1, int x2, int y2):base(x1, y1)
{
xEnd = x2; yEnd = y2;
}
}
class Program
{
static void Main()
{
DemoPoint point1= new DemoPoint(); //вызов конструктора по умолчанию
DemoPoint point2= new DemoPoint(5, 5); //вызов конструктора с параметрами
point1.Show();
point2.Show();
DemoLine line1 = new DemoLine();//вызов конструктора по умолчанию
DemoLine line2 = new DemoLine(4, 4); //вызов конструктора с двумя параметрами
//вызов конструктора с четырьмя параметрами
DemoLine line3 = new DemoLine(2, 2, 10, 10);
line1.Show();
line2.Show();
line3.Show();
}
}
В этом случае ключевое слово base действует подобно ссылке this, за исключением того, что ссылка base всегда указывает на базовый класс для производного класса, в котором она используется. В этом случае формат ее записи выглядит следующим образом:
base.член_класса
Здесь в качестве элемента член_класса можно указывать либо метод, либо поле экземпляра. Эта форма ссылки base наиболее применима в тех случаях, когда имя члена в производном классе скрывает член с таким же именем в базовом классе.
class DemoPoint
{
protected int x;
protected int y;
public void Show()
{
Console.Write("({0}, {1})",x, y);
}
public DemoPoint (int x, int y)//конструктор базового класса
{
this.x=x; this.y=y;
}
}
class DemoLine : DemoPoint
{
public int xEnd;
public int yEnd;
new public void Show()
{
base.Show(); //вызов члена базового класса
Console.WriteLine("-({0}, {1})", xEnd, yEnd);
}
public DemoLine(int x1, int y1, int x2, int y2):base(x1, y1) //конструктор производного класса
{
xEnd = x2; yEnd = y2;
}
}
class Program
{
static void Main()
{
DemoLine line = new DemoLine( 2, 2, 10, 10);
line.Show();
}
}
Несмотря на то, что метод Show в классе DemoLine скрывает одноименный метод в классе DemoPoint, ссылка base позволяет получить доступ к методу Show в базовом классе. Аналогично с помощью ссылки base можно получить доступ к одноименным полям базового класса.
До сих пор мы рассматривали простой тип иерархии классов, который состоит из одного базового и одного производного класса. В общем случае можно построить иерархию классов, состоящую из любого количества уровней наследования. Рассмотрим следующую иерархию классов:
(рис 15.2) 15_02
class DemoPoint
{
protected int x;
protected int y;
public void Show()
{
Console.WriteLine("точка на плоскости: ({0}, {1})",x, y);
}
public DemoPoint (int x, int y)
{
this.x=x; this.y=y;
}
}
class DemoShape : DemoPoint
{
protected int z;
new public void Show()
{
Console.WriteLine("точка в пространстве: ({0}, {1}, {2})", x, y, z);
}
public DemoShape(int x, int y, int z):base(x, y)
{
this.z=z;
}
}
class DemoLine : DemoPoint
{
protected int x2;
protected int y2;
new public void Show()
{
Console.WriteLine("отрезок на плоскости: ({0}, {1})-({2},{3})",x,y, x2, y2);
}
public DemoLine(int x1, int y1, int x2, int y2):base(x1, y1)
{
this.x2 = x2; this.y2 = y2;
}
}
class DemoTriangle: DemoLine
{
protected int x3;
protected int y3;
new public void Show()
{
Console.WriteLine("треугольник на плоскости: ({0}, {1})-({2},{3})-({4},{5})",x,y, x2, y2, x3, y3);
}
public DemoTriangle(int x1, int y1, int x2, int y2, int x3, int y3):base(x1, y1, x2, y2)
{
this.x3 = x3; this.y3 = y3;
}
}
class Program
{
static void Main()
{
DemoPoint point = new DemoPoint(1,1);
point.Show();
DemoShape pointShape = new DemoShape(1,1,1);
pointShape.Show();
DemoLine line = new DemoLine( 2, 2, 10, 10);
line.Show();
DemoTriangle triangle = new DemoTriangle (0,0,0,3,4,0);
triangle.Show();
}
}
С# является языком со
class DemoPoint
{
public int x;
public int y;
public void Show()
{
Console.WriteLine("точка на плоскости: ({0}, {1})",x, y);
}
public DemoPoint (int x, int y)
{
this.x=x; this.y=y;
}
}
class DemoShape : DemoPoint
{
public int z;
new public void Show()
{
Console.WriteLine("точка в пространстве: ({0}, {1}, {2})", x, y, z);
}
public DemoShape(int x, int y, int z):base(x, y)
{
this.z=z;
}
}
class Program
{
static void Main()
{
DemoPoint point1 = new DemoPoint(0,1);
Console.WriteLine("({0}, {1})",point1.x,point1.y);
DemoShape pointShape = new DemoShape(2,3,4);
Console.WriteLine("({0}, {1}, {2})",pointShape.x, pointShape.y, pointShape.z);
DemoPoint point2=pointShape; //допустимая операция
//ошибка - не соответствие типов указателей
//pointShape=point1;
Console.WriteLine("({0}, {1})", point2.x, point2.y);
//ошибка, т.к. в классе DemoPoint нет поля z
//Console.WriteLine("({0}, {1}, {2})", point2.x, point2.y, point2.z);
}
}
Ошибка возникнет и при попытке через объект point2 обратиться к методу Show. Например, point2.Show(). В этом случае компилятор не сможет определить, какой метод Show вызвать - для базового или для производного класса. Для решения данной проблемы можно воспользоваться таким понятием как полиморфизм, который основывается на механизме виртуальных методов.
Виртуальный метод - это метод, который объявлен в базовом классе с использованием ключевого слова virtual, и затем переопределен в производном классе с помощью ключевого слова override. При этом если реализована многоуровневая иерархия классов, то каждый производный класс может иметь свою собственную версию виртуального метода. Этот факт особенно полезен в случае, когда доступ к объекту производного класса осуществляется через ссылочную переменную базового класса. В этой ситуации С# сам выбирает какую версию виртуального метода нужно вызвать. Этот выбор производится по типу объекта, на которую ссылается данная ссылка. Например:
class DemoPoint //базовый класс
{
protected int x;
protected int y;
public virtual void Show() //виртуальный метод
{
Console.WriteLine("точка на плоскости: ({0}, {1})",x, y);
}
public DemoPoint (int x, int y)
{
this.x=x; this.y=y;
}
}
class DemoShape : DemoPoint //производный класс
{
protected int z;
public override void Show() //перегрузка виртуального метода
{
Console.WriteLine("точка в пространстве: ({0}, {1}, {2})", x, y, z);
}
public DemoShape(int x, int y, int z):base(x, y) //конструктор производного класса
{
this.z=z;
}
}
class DemoLine : DemoPoint //производный класс
{
protected int x2;
protected int y2;
public override void Show() //перегрузка виртуального метода
{
Console.WriteLine("отрезок на плоскости: ({0}, {1})-({2},{3})",x,y, x2, y2);
}
public DemoLine(int x1, int y1, int x2, int y2):base(x1, y1)
{
this.x2 = x2; this.y2 = y2;
}
}
class Program
{
static void Main()
{
DemoPoint point1 = new DemoPoint(0,1);
point1.Show();
DemoShape pointShape = new DemoShape(2,3,4);
pointShape.Show();
DemoLine line = new DemoLine(0,0, 10, 10);
line.Show();
Console.WriteLine();
//использование ссылки базового класса на объекты производных классов
DemoPoint point2=pointShape;
point2.Show();
point2=line;
point2.Show();
}
}
Таким образом, благодаря полиморфизму через ссылочную переменную возможно обращаться к объектам разного типа, а также с помощью одного и того же имени выполнять различные действия.
Иногда полезно создать базовый класс, определяющий только своего рода "пустой бланк", который унаследуют все производные классы, причем каждый из них заполнит этот "бланк" собственной информацией. Такой класс определяет структуру методов, которые производные классы должны реализовать, но сам при этом не обеспечивает реализации этих методов. Подобная ситуация может возникнуть, когда базовый класс попросту не в состоянии реализовать метод. В данной ситуации разрабатываются целые абстрактные классы.
virtual не нужно. Более того, если вы попытаетесь использовать два спецификатора одновременно, abstract и virtual, то компилятор выдаст сообщение об ошибке.
abstract применять в сочетании со спецификатором static. И объясните почему?Если класс содержит один или несколько абстрактных классов, то его также нужно объявить как абстрактный, используя спецификатор abstract перед class. Поскольку абстрактный класс полностью не реализован, то невозможно создать экземпляр класса с помощью операции new. Например, если класс Demo определен как абстрактный, то попытка создать экземпляр класса Demo повлечет ошибку:
Demo a = new Demo();
Однако, можно создать массив ссылок, используя этот же абстрактный класс:
Demo [] Ob=new Demo[5];
Если производный класс наследует абстрактный, то он должен полностью переопределить все abstract наследуется до тех пор, пока в производном классе не будут реализованы все
Рассмотрим пример использования
abstract class Demo //абстрактный класс
{
abstract public void Show();//абстрактный метод
abstract public double Dlina();//абстрактный метод
}
class DemoPoint:Demo //производный класс от абстрактного
{
protected int x;
protected int y;
public DemoPoint (int x, int y)
{
this.x=x; this.y=y;
}
public override void Show() //переопределение абстрактного метода
{
Console.WriteLine("точка на плоскости: ({0}, {1})",x, y);
}
public override double Dlina() //переопределение абстрактного метода
{
return Math.Sqrt(x*x+y*y);
}
}
class DemoShape : DemoPoint //производный класс
{
protected int z;
public DemoShape(int x, int y, int z):base(x, y)
{
this.z=z;
}
public override void Show() //переопределение абстрактного метода
{
Console.WriteLine("точка в пространстве: ({0}, {1}, {2})", x, y, z);
}
public override double Dlina() //переопределение абстрактного метода
{
return Math.Sqrt(x*x+y*y+z*z);
}
}
class DemoLine : DemoPoint //производный класс
{
protected int x2;
protected int y2;
public DemoLine(int x1, int y1, int x2, int y2):base(x1, y1)
{
this.x2 = x2; this.y2 = y2;
}
public override void Show() //переопределение абстрактного метода
{
Console.WriteLine("отрезок на плоскости: ({0}, {1})-({2},{3})",x,y, x2, y2);
}
public override double Dlina() //переопределение абстрактного метода
{
return Math.Sqrt((x-x2)*(x-x2)+(y-y2)*(y-y2));
}
}
class Program
{
static void Main()
{
Demo [] Ob=new Demo[5]; //массив ссылок
//заполнения массива ссылками на объекты производных классов
Ob[0]=new DemoPoint(1,1);
Ob[1]=new DemoShape(1,1,1);
Ob[2]=new DemoLine(0,3,4,0);
Ob[3]=new DemoLine(2,1,2,10);
Ob[4]=new DemoPoint(0,100);
foreach (Demo a in Ob) //просмотр массива
{
a.Show();
Console.WriteLine("Dlina: {0:f2}\n", a.Dlina());
}
}
}
В С# есть ключевое слово sealed, позволяющее описать класс, от которого запрещено наследование. Например:
sealed class Demo { … }
class newDemo: Demo { … } // ошибка
sealed и abstract при описании класса, и почему?Задание 1
Figure с методами вычисления площади и периметра, а также методом, выводящим информацию о фигуре на экран.Rectangle (прямоугольник), Circle (круг), Triangle (треугольник) со своими методами вычисления площади и периметра.n фигур и вывести полную информацию о фигурах на экран.Задание 2
Function с методом вычисления значения функции y=f(x) в заданной точке.Line (y=ax+b), Kub (y=ax2+bx+c), Hyperbola со своими методами вычисления значения в заданной точке.х.Задание 3
Издание с методами позволяющим вывести на экран информацию об издании, а также определить является ли данное издание искомым.Книга (название, фамилия автора, год издания, издательство), Статья (название, фамилия автора, название журнала, его номер и год издания), Электронный ресурс (название, фамилия автора, ссылка, аннотация) со своими методами вывода информации на экран.n изданий, вывести полную информацию из каталога, а также организовать поиск изданий по фамилии автора.Задание 4
Trans с методами позволяющим вывести на экран информацию о транспортном средстве, а также определить грузоподъемность транспортного средства.Легковая_машина (марка, номер, скорость, грузоподъемность), Мотоцикл (марка, номер, скорость, грузоподъемность, наличие коляски, при этом если коляска отсутствует, то грузоподъемность равна 0), Грузовик (марка, номер, скорость, грузоподъемность, наличие прицепа, при этом если есть прицеп, то грузоподъемность увеличивается в два раза) со своими методами вывода информации на экран, и определения грузоподъемности.n машин, вывести полную информацию из базы на экран, а также организовать поиск машин, удовлетворяющих требованиям грузоподъемности.Задание 5
Persona с методами, позволяющим вывести на экран информацию о персоне, а также определить ее возраст (на момент текущей даты).Абитуриент (фамилия, дата рождения, факультет), Студент (фамилия, дата рождения, факультет, курс), Преподавать (фамилия, дата рождения, факультет, должность, стаж), со своими методами вывода информации на экран, и определения возраста.n персон, вывести полную информацию из базы на экран, а также организовать поиск персон, чей возраст попадает в заданный диапазон.Задание 6
Товар с методами, позволяющим вывести на экран информацию о товаре, а также определить, соответствует ли она сроку годности на текущую дату.Продукт (название, цена, дата производства, срок годности), Партия (название, цена, количество шт, дата производства, срок годности), Комплект (названия, цена, перечень продуктов) со своими методами вывода информации на экран, и определения соответствия сроку годности.n товаров, вывести полную информацию из базы на экран, а также организовать поиск просроченного товара (на момент текущей даты).Задание 7
Товар с методами, позволяющими вывести на экран информацию о товаре, а также определить, соответствует ли она искомому типу.Игрушка (название, цена, производитель, материал, возраст, на который рассчитана), Книга (название, автор, цена, издательство, возраст, на который рассчитана), Спорт-инвентарь (название, цена, производитель, возраст, на который рассчитана), со своими методами вывода информации на экран, и определения соответствия искомому типу.n товаров, вывести полную информацию из базы на экран, а также организовать поиск товаров определенного типа.Задание 8
Телефонный_справочник с методами, позволяющими вывести на экран информацию о записях в телефонном справочнике, а также определить соответствие записи критерию поиска.Персона (фамилия, адрес, номер телефона), Организация (название, адрес, телефон, факс, контактное лицо), Друг (фамилия, адрес, номер телефона, дата рождения) со своими методами вывода информации на экран, и определения соответствия искомому типу.n товаров, вывести полную информацию из базы на экран, а также организовать поиск в базе по фамилии.Задание 9
Клиент с методами, позволяющими вывести на экран информацию о клиентах банка, а также определить соответствие клиента критерию поиска.Вкладчик (фамилия, дата открытия вклада, размер вклада, процент по вкладу), Кредитор (фамилия, дата выдачи кредита, размер кредита, процент по кредиту, остаток долга), Организация (название, дата открытия счета, номер счета, сумма на счету) со своими методами вывода информации на экран, и определения соответствия дате (открытия вклада, выдаче кредита, открытия счета).n клиентов, вывести полную информацию из базы на экран, а также организовать поиск клиентов, начавших сотрудничать с банком в заданную дату.Задание 10
Программное_обеспечение с методами, позволяющими вывести на экран информацию о программном обеспечении, а также определить соответствие возможности использования (на момент текущей даты).Свободное (название, производитель), Условно-бесплатное (название, производитель, дата установки, срок бесплатного использования), Коммерческое (название, производитель, цена, дата установки, срок использования) со своими методами вывода информации на экран, и определения возможности использования на текущую дату.n видов программного обеспечения, вывести полную информацию из базы на экран, а также организовать поиск программного обеспечения, которое допустимо использовать на текущую дату.Используя дополнительную литературу и Интернет, рассмотрите следующие темы:
is, as, typeof.Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.