Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Переменные базового класса и производного класса




Наследование. Абстрактные классы

Класс в С# может иметь произвольное количество потомков и только одного предка. При описании класса имя его предка записывается в заголовке класса после двоеточия. Если имя предка не указано, предком считается базовый класс всей иерархии System.Object. Синтаксис наследования:

 

[атрибуты] [спецификаторы] class имя_класса [: предки]

{ тело_класса}

Класс, который наследуется, называется базовым. Класс, который наследует базовый класс, называется производным. Производный класс, наследует все переменные, методы, свойства, операторы и индексаторы, определенные в базовом классе, кроме того в производный класс могут быть добавлены уникальные элементы или переопределены существующие.

Использование защищенного доступа

Public - открыты для доступа. Если убрать public, то поля автоматически станут закрытыми для доступа (private), в том числе и для доступа из производного класса. Решить проблему доступа к закрытым полям базового класса из производного можно двумя способами: используя свойства класса или спецификатор protected. При объявлении какого-то члена класса с помощью спецификатора protected, он становится закрытым для всех классов, кроме производных.

 

Наследование конструкторов

В иерархии классов как базовые, так и производные классы могут иметь собственные конструкторы. При этом конструктор базового класса создает часть объекта, соответствующую базовому классу, а конструктор производного класса — часть объекта, соответствующую производному классу. Так как базовый класс не имеет доступа к элементам производного класса, то их конструкторы должны быть раздельными.

Если же конструкторы определены и в базовом, и в производном классе, то процесс создания объектов несколько усложняется, т.к. должны выполниться конструкторы обоих классов. В этом случае используется ключевое слово base, которое имеет два назначения:

1) позволяет вызвать конструктор базового класса:

Производный класс может вызывать конструктор, определенный в его базовом классе, используя расширенную форму объявления конструктора и ключевое слово base. Формат расширенного объявления:

 

конструктор_производного_класса (список_параметров): base (список_аргументов)

{ тело конструктора }

 

где с помощью элемента списка аргументов передаются параметры конструктору базового класса.

 

В общем случае с помощью ключевого слова base можно вызвать конструктор любой формы, определенный в базовом классе. Реально же выполнится тот конструктор, параметры которого будут соответствовать переданным при вызове аргументам. Например:

 

2) позволяет получить доступ к члену базового класса, который скрыт "за" членом производного класса.

В этом случаеключевое слово base действует подобно ссылке this, за исключением того, что ссылка base всегда указывает на базовый класс для производного класса, в котором она используется. В этом случае формат ее записи выглядит следующим образом:

 

base.член_класса

 

Здесь в качестве элемента член_класса можно указывать либо метод, либо поле экземпляра. Эта форма ссылки base наиболее применима в тех случаях, когда имя члена в производном классе скрывает член с таким же именем в базовом классе.

 

Несмотря на то, что метод Show в классе DemoLine скрывает одноименный метод в классе DemoPoint, ссылка base позволяет получить доступ к методу Show в базовом классе. Аналогично с помощью ссылки 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 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();

}

}

Таким образом, благодаря полиморфизму через ссылочную переменную возможно обращаться к объектам разного типа, а также с помощью одного и того же имени выполнять различные действия.

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...