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

Доступ к членам класса и наследование




Наследование

Наследование является одним из трех основополагающих принципов объектно-ориентированного программирования, поскольку оно допускает создание иерархических классификаций. Благодаря наследованию можно создать общий класс, в котором определяются характерные особенности, присущие множеству связанных элементов. От этого класса могут затем наследовать другие, более конкретные классы, добавляя в него свои индивидуальные особенности.

В языке С# класс, который наследуется, называется базовым, а класс, который наследует, - производным. Следовательно, производный класс представляет собой спе­циализированный вариант базового класса. Он наследует все переменные, методы, свойства и индексаторы, опреде­ляемые в базовом классе, добавляя к ним свои собственные элементы.

Основы наследования

Поддержка наследования в С# состоит в том, что в объяв­ление одного класса разрешается вводить другой класс. Для этого при объявлении производного класса указывается базовый класс. Рассмотрим для начала простой пример. Ниже приведен класс TwoDShape, содержащий ширину и высоту двухмерного объекта, например квадрата, пря­моугольника, треугольника и т.д.

Листинг 11.1

// Класс для двумерных объектов.

class TwoDShape

{

public double Width;

public double Height;

 

public void ShowDim()

{

Console.WriteLine("Ширина и высота равны " +

Width + " и " + Height);

}

}

Класс TwoDShape может стать базовым, т.е. отправной точкой для создания классов, описывающих конкретные типы двумерных объектов. Например, в приведенной ниже программе класс TwoDShape служит для порождения производного класса Triangle. Обратите особое внимание на объявление класса Triangle.

Листинг 11.2

// Пример простой иерархии классов.

 

using System;

 

// Класс для двумерных объектов.

class TwoDShape

{

public double Width;

public double Height;

 

public void ShowDim()

{

Console.WriteLine("Ширина и высота равны " +

Width + " и " + Height);

}

}

 

// Класс Triangle, производный от класса TwoDShape.

class Triangle: TwoDShape {

public string Style; // тип треугольника

 

// Возвратить площадь треугольника.

public double Area() {

return Width * Height / 2;

}

 

// Показать тип треугольника.

public void ShowStyle() {

Console.WriteLine("Треугольник " + Style);

}

}

 

class Shapes

{

static void Main()

{

Triangle t1 = new Triangle();

Triangle t2 = new Triangle();

 

t1.Width = 4.0;

t1.Height = 4.0;

t1.Style = "равнобедренный";

 

t2.Width = 8.0;

t2.Height = 12.0;

t2.Style = "прямоугольный";

 

Console.WriteLine("Сведения об объекте t1: ");

t1.ShowStyle();

t1.ShowDim();

Console.WriteLine("Площадь равна " + t1.Area());

 

Console.WriteLine();

 

Console.WriteLine("Сведения об объекте t2: ");

t2.ShowStyle();

t2.ShowDim();

Console.WriteLine("Площадь равна " + t2.Area());

}

}

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

Сведения об объекте tl:

Треугольник равнобедренный

Ширина и высота равны 4 и 4

Площадь равна 8

 

Сведения об объекте t2:

Треугольник прямоугольный

Ширина и высота равны 8 и 12

Площадь равна 48

В классе Triangle создается особый тип объекта класса TwoDShape (в данном слу­чае - треугольник). Кроме того, в класс Triangle входят все члены класса TwoDShape, к которым, в частности, добавляются методы Area() и ShowStyle(). Так, описание типа треугольника сохраняется в переменной Style, метод Area() рассчитывает и воз­вращает площадь треугольника, а метод ShowStyle() отображает тип треугольника.

Обратите внимание на синтаксис, используемый в классе Triangle для наследова­ния класса TwoDShape.

class Triangle: TwoDShape {

Этот синтаксис может быть обобщен. Всякий раз, когда один класс наследует от другого, после имени базового класса указывается имя производного класса, отделяе­мое двоеточием. В С# синтаксис наследования класса удивительно прост и удобен в ис­пользовании.

В класс Triangle входят все члены его базового класса TwoDShape, и поэтому в нем переменные Width и Height доступны для метода Area(). Кроме того, объекты tl и t2 в методе Main() могут обращаться непосредственно к переменным Width и Height, как будто они являются членами класса Triangle. На рисунке 1 схематически показано, каким образом класс TwoDShape вводится в класс Triangle.

Рисунок 1 – Схема представления класса Triangle

Несмотря на то что класс TwoDShape является базовым для класса Triangle, в то же время он представляет собой совершенно независимый и самодостаточный класс. Если класс служит базовым для производного класса, то это совсем не означает, что он не может быть использован самостоятельно. Например, следующий фрагмент кода считается вполне допустимым.

TwoDShape shape = new TwoDShape ();

shape.Width =10;

shape.Height = 20;

 

shape.ShowDim();

Разумеется, объект класса TwoDShape никак не связан с любым из классов, произво­дных от класса TwoDShape, и вообще не имеет к ним доступа.

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

class имя_производного_класса: имя_базового_класса

{

// тело класса

}

Для любого производного класса можно указать только один базовый класс. В С# не предусмотрено наследование нескольких базовых классов в одном производном клас­се. (В этом отношении С# отличается от С++, где допускается наследование нескольких базовых классов. Данное обстоятельство следует принимать во внимание при переносе кода С++ в С#.) Тем не менее можно создать иерархию наследования, в которой про­изводный класс становится базовым для другого производного класса. (Разумеется, ни один из классов не может быть базовым для самого себя как непосредственно, так и косвенно.) Но в любом случае производный класс наследует все члены своего базового класса, в том числе переменные экземпляра, методы, свойства и индексаторы.

Главное преимущество наследования заключается в следующем: как только будет создан базовый класс, в котором определены общие для множества объектов атрибу­ты, он может быть использован для создания любого числа более конкретных произ­водных классов. А в каждом производном классе может быть точно выстроена своя собственная классификация. В качестве примера ниже приведен еще один класс, про­изводный от класса TwoDShape и инкапсулирующий прямоугольники.

Листинг 11.3

// Класс для прямоугольников, производный от класса TwoDShape.

class Rectangle: TwoDShape

{

// Возвратить логическое значение true, если

// прямоугольник является квадратом.

public bool IsSquare()

{

if(Width == Height) return true;

return false;

}

 

// Возвратить площадь прямоугольника.

public double Area()

{

return Width * Height;

}

}

В класс Rectangle входят все члены класса TwoDShape, к которым добавлен метод IsSquare(), определяющий, является ли прямоугольник квадратом, а также метод Area(), вычисляющий площадь прямоугольника.

Доступ к членам класса и наследование

Члены класса зачастую объявляются закрытыми, чтобы исключить их несанкционированное или незаконное использование. Но наследование класса не отменяет ограничения, накладываемые на доступ к закрытым членам класса. Поэтому если в производный класс и входят все члены его базового класса, в нем все равно оказываются недоступными те члены базового класса, которые являются закры­тыми. Так, если сделать закрытыми переменные класса TwoDShape, они станут недо­ступными в классе Triangle, как показано ниже.

Листинг 11.4

// Доступ к закрытым членам класса не наследуется.

// Этот пример кода не подлежит компиляции.

 

using System;

 

// Класс для двумерных объектов.

class TwoDShape

{

double Width; // теперь это закрытая переменная

double Height; // теперь это закрытая переменная

 

public void ShowDim()

{

Console.WriteLine("Ширина и высота равны " +

Width + " и " + Height);

}

}

 

// Класс Triangle производный от класса TwoDShape.

class Triangle: TwoDShape

{

public string Style; // тип треугольника

 

// Возвратить площадь треугольника.

public double Area()

{

return Width * Height / 2; // Ошибка. Доступ к закрытому

// члену класса запрещен

}

 

// Показать тип треугольника.

public void ShowStyle() {

Console.WriteLine("Треугольник " + Style);

}

}

Класс Triangle не будет компилироваться, потому что обращаться к перемен­ным Width и Height из метода Area() запрещено. А поскольку переменные Width и Height теперь являются закрытыми, то они доступны только для других членов своего класса, но не для членов производных классов.

Закрытый член класса остается закрытым в своем классе. Он не доступен из кода за пределами своего класса, включая и производные классы.

На первый взгляд, ограничение на доступ к частным членам базового класса из про­изводного класса кажется трудно преодолимым, поскольку оно не дает во многих слу­чаях возможности пользоваться частными членами этого класса. Но на самом деле это не так. Для преодоления данного ограничения в С# предусмотрены разные способы. Один из них состоит в использовании защищенных (protected) членов класса, рас­сматриваемых в следующем разделе, а второй - в применении открытых свойств для доступа к закрытым данным.

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

Ниже приведен вариант класса TwoDShape, в котором переменные Width и Height превращены в свойства. По ходу дела в этом классе выполняется проверка: являются ли положительными значения свойств Width и Height. Это дает, например, возможность указывать свойства Width и Height в качестве координат формы в любом квадранте прямоугольной системы координат, не получая заранее их абсолютные значения.

Листинг 11.5

// Использовать открытые свойства для установки и

// получения значений закрытых членов класса.

 

using System;

 

// Класс для двумерных объектов.

class TwoDShape

{

double width; // это закрытая переменная

double height; // это закрытая переменная

 

// Свойства ширины и высоты двумерного объекта.

public double Width

{

get { return width; }

set { width = value < 0? -value: value; }

}

 

public double Height

{

get { return height; }

set { height = value < 0? -value: value; }

}

 

public void ShowDim()

{

Console.WriteLine("Ширина и высота равны " +

Width + " и " + Height);

}

}

 

// Класс для треугольников, производный от класса TwoDShape.

class Triangle: TwoDShape

{

public string Style; // тип треугольника

 

// Возвратить площадь треугольника.

public double Area()

{

return Width * Height / 2;

}

 

// Показать тип треугольника.

public void ShowStyle()

{

Console.WriteLine("Triangle is " + Style);

}

}

 

class Shapes2 {

static void Main()

{

Triangle t1 = new Triangle();

Triangle t2 = new Triangle();

 

t1.Width = 4.0;

t1.Height = 4.0;

t1.Style = "равнобедренный";

 

t2.Width = 8.0;

t2.Height = 12.0;

t2.Style = "прямоугольный";

 

Console.WriteLine("Сведения об объекте t1: ");

t1.ShowStyle();

t1.ShowDim();

Console.WriteLine("Площадь равна " + t1.Area());

 

Console.WriteLine();

 

Console.WriteLine("Сведения об объекте t2: ");

t2.ShowStyle();

t2.ShowDim();

Console.WriteLine("Площадь равна " + t2.Area());

}

}

В этом варианте свойства Width и Height предоставляют доступ к закрытым чле­нам pri_width и pri_height класса TwoDShape, в которых фактически хранятся значения ширины и высоты двумерного объекта. Следовательно, значения членов pri_width и pri_height класса TwoDShape могут быть установлены и получены с помощью соответствующих открытых свойств, несмотря на то, что сами эти члены по-прежнему остаются закрытыми.

Базовый и производный классы иногда еще называют суперклассом и подклассом соответственно. Эти термины происходят из практики программирования на Java. То, что в Java называется суперклассом, в С# обозначается как базовый класс. А то, что в Java называется подклассом, в С# обозначается как производный класс. Оба ряда тер­минов часто применяются к классу в обоих языках программирования, но в этой книге по-прежнему употребляются общепринятые в С# термины базового и производного классов, которые принято употреблять и в С++.

Поделиться:





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



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