- Автор темы
- #1
Наследование.
Итак, настало время ознакомиться с таким вечнозеленым понятием объектно-ориентированного программирования как наследование. Данный термин вам уже известен из курса языка программирования С++.Теперь мы его рассмотрим в контексте С#. Начнем с синтаксиса применяемого для наследования:
Обратите внимание на следующие отличия в механизме наследования C# от С++:
Из примера видно, что принципы наследования работают достаточно схоже с С++.
Спецификатор доступа protected.
Спецификатор доступа protected вам уже известен из курса С++. В С# он работает точно также. Например:
Конструктора при наследовании.
В иерархии наследования допускается, чтобы базовые и дочерние классы имели свои собственные конструкторы. В этом случае при создании объекта от наследованного класса сначала вызываются конструктора базовых классов в порядке наследования и только потом конструктор потомка (фактически “сверху вниз”). Например:
В данном примере мы не передавали параметры в конструкторы базовых классов Base и Child, а если нам нужно передавать? Например:
Для того чтобы решить возникшую проблему нужно использовать следующий синтаксис:
Для вызова конструктора базового класса используется ключевое слово base. Например:
Как видно из примера приведенного выше с помощью ключевого слова base можно в дочернем классе обратиться к унаследованным членам базового. Его можно использовать только в классе-потомке. Вы обратили внимание на появившееся предупреждение при компиляции программы приведенной выше?
Оно сообщает о том, что метод Show() класса MobileTelephone перекрывает версию Show из базового класса Device. Для того чтобы убрать его необходимо указать ключевое слово new перед Show внутри класса MobileTelephone. Например (фрагмент из программы):
Ссылки на объекты базового и дочернего классов.
С# - это строго типизированный язык. Это правда, для вас не новость. Например, это ярко проявляется при операции присваивания. Рассмотрим пример ошибки связанной с преобразованиями типов:
В данной программе возникает ошибка на этапе компиляции, так как невозможно автоматическое преобразование по отношению к переменным ссылочного типа (оно распространяется только на переменные обычных типов). Отсюда можно сделать вывод, что ссылочная переменная одного класса не может ссылаться на объект другого класса. Однако из этого правила есть одно исключение. Ссылочной переменной базового класса можно присвоить ссылку на объект дочернего класса, в этом случае через неё можно будет обратиться к тем членам производного, которые были унаследованы из базового. Например:
Запрет наследования
Иногда при разработке класса нужно запретить возможность наследования от него. Для этого используется ключевое слово sealed, которое указывается при объявлении класса. Данный механизм может понадобиться при разработке какого-то служебного класса. Например, уже известный вам класс Console библиотеки .NET Framework объявлен с использованием sealed. Рассмотрим программу, демонстрирующую этот принцип:
Итак, настало время ознакомиться с таким вечнозеленым понятием объектно-ориентированного программирования как наследование. Данный термин вам уже известен из курса языка программирования С++.Теперь мы его рассмотрим в контексте С#. Начнем с синтаксиса применяемого для наследования:
Код:
class имя_класса_потомка:имя_базового_класса{
// тело класса
}
Обратите внимание на следующие отличия в механизме наследования C# от С++:
- В качестве базового класса при наследовании может быть указан только один класс. Это означает, что в C# нет механизма множественного наследования. Если же необходимо всё-таки выполнить его то тогда можно воспользоваться понятием интерфейса, которое будет рассмотрено позже.
- При наследовании не указываются спецификаторы доступа как в C++.
Код:
using System;
class Base{
private int x;
public int y;
public void Info(){
Console.WriteLine("Info In Base Class !!! y = " +y);
Info2();
}
private void Info2(){
x = 456;
Console.WriteLine("Info 2 In Base Class !!! x = "+x);
}
}
class Child:Base{
int z; // по умолчанию public
public int w;
public void Set(int a,int b,int c){
z = a;
w = b;
y = c;
// Ошибка на этапе компиляции нет доступа к private - членам базового класса.
// x = 123 ;
// Ошибка на этапе компиляции нет доступа к private - методам базового класса.
// Info2();
}
public void Show(){
Console.WriteLine("Show In Child Class");
Info();
Console.WriteLine("z = {0} w = {1}",z,w);
}
}
class Sample
{
static void Main()
{
try{
Base obj = new Base();
obj.y = 789;
obj.Info();
// Ошибка на этапе компиляции нет доступа к private - методам класса.
//obj.Info2();
Child obj2 = new Child();
obj2.Set(1,2,3);
obj2.Info();
obj2.y = 555;
obj2.Show();
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
Из примера видно, что принципы наследования работают достаточно схоже с С++.
Спецификатор доступа protected.
Спецификатор доступа protected вам уже известен из курса С++. В С# он работает точно также. Например:
Код:
using System;
class Person{
private string name;
private string surname;
protected string comment;
public void Set(string name,string surname,string comment){
this.name = name;
this.surname = surname;
this.comment = comment;
}
public void Show(){
Console.WriteLine("Person: name = {0} surname = {1}
comment = {2}",name,surname,comment);
}
}
class Student:Person{
private string group;
public void ShowInStudent(){
Show();
Console.WriteLine("Student: group = "+group);
}
public void SetInStudent(string name,string surname,string comment,string group){
Set(name,surname,comment);
this.group = group;
}
public void ModifyComment(string comment){
// Это не ошибка так как protected - члены доступны и в потомках !!!
this.comment = comment;
// Ошибка на этапе компиляции нет доступа к private - членам базового класса.
//this.name = "Вася"
}
}
class Sample
{
static void Main()
{
try{
Student obj = new Student();
obj.SetInStudent("Вася","Пупкин","Просто хороший человек","17ОПС");
obj.ShowInStudent();
obj.ModifyComment("Студент");
Console.WriteLine("\n\n");
obj.ShowInStudent();
// Ошибка на этапе компиляции нет доступа к protected - членам
// вне класса и его потомков
// obj.comment = "Просто Комментарий";
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
Конструктора при наследовании.
В иерархии наследования допускается, чтобы базовые и дочерние классы имели свои собственные конструкторы. В этом случае при создании объекта от наследованного класса сначала вызываются конструктора базовых классов в порядке наследования и только потом конструктор потомка (фактически “сверху вниз”). Например:
Код:
using System;
class Base{
public Base(){
Console.WriteLine("Base");
}
}
class Child:Base{
public Child(){
Console.WriteLine("Child");
}
}
class Child2:Child{
public Child2(){
Console.WriteLine("Child2");
}
}
class Sample
{
static void Main()
{
try{
Child2 obj = new Child2();
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
В данном примере мы не передавали параметры в конструкторы базовых классов Base и Child, а если нам нужно передавать? Например:
Код:
using System;
class A{
public int a;
public A(int f){
a = f;
}
}
class B:A{
public int b;
// Ошибка компиляции не передаются параметры в конструктор базового класса !!!
B(int x,int y){
a = x;
b = y;
}
}
class Samples
{
static void Main()
{
try{
B obj = new B(12,23);
Console.WriteLine(obj.a+" "+obj.b);
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
Для того чтобы решить возникшую проблему нужно использовать следующий синтаксис:
Код:
имя_конструктора_потомка(список параметров):base(параметры_передаваемые_в_базовый_класс){
// тело конструктора
}
Для вызова конструктора базового класса используется ключевое слово base. Например:
Код:
using System;
class Device{
protected string title;
protected float weight;
public Device(string title,float weight){
this.title = title;
this.weight = weight;
}
public void Show(){
Console.WriteLine("Название: "+title+" Вес:"+weight);
}
}
class MobileTelephone:Device{
private string brand;
private bool antenna;
// Передача параметров в базовый конструктор
public MobileTelephone(string title,float weight,string brand,
bool antenna):base(title,weight){
this.brand = brand;
this.antenna = antenna;
}
public void Show(){
// Обращение к методу базового класса, используя ключевое слово base
base.Show();
string temp;
Console.WriteLine(
"Производитель: "+brand+" Антенна:"+(temp=antenna==true?"есть":"нет"));
}
}
class Samples
{
static void Main()
{
try{
MobileTelephone obj = new
MobileTelephone("GD93",0.89f,"Panasonic",true);
obj.Show();
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
Как видно из примера приведенного выше с помощью ключевого слова base можно в дочернем классе обратиться к унаследованным членам базового. Его можно использовать только в классе-потомке. Вы обратили внимание на появившееся предупреждение при компиляции программы приведенной выше?
warning CS0108:
The keyword new is required on 'MobileTelephone.Show()'
because it hides inherited member 'Device.Show()'
Оно сообщает о том, что метод Show() класса MobileTelephone перекрывает версию Show из базового класса Device. Для того чтобы убрать его необходимо указать ключевое слово new перед Show внутри класса MobileTelephone. Например (фрагмент из программы):
Код:
class MobileTelephone:Device{
private string brand;
private bool antenna;
// Передача параметров в базовый конструктор
public MobileTelephone(string title,float weight,string brand,bool antenna):base(title,weight){
this.brand = brand;
this.antenna = antenna;
}
new public void Show(){ // Ещё одно применение new
// Обращение к методу базового класса, используя ключевое слово base
base.Show();
string temp;
Console.WriteLine(
"Производитель: "+brand+" Антенна:"+(temp=antenna==true?"есть":"нет"));
}
}
Ссылки на объекты базового и дочернего классов.
С# - это строго типизированный язык. Это правда, для вас не новость. Например, это ярко проявляется при операции присваивания. Рассмотрим пример ошибки связанной с преобразованиями типов:
Код:
using System;
class A{
public int a = 12;
}
class B{
public int b = 1;
}
class Samples
{
static void Main()
{
try{
A obj = new A();
B obj2 = new B();
B test;
test = obj2;
Console.WriteLine("Класс B:"+test.b);
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
test = obj; // Ошибка на этапе компиляции.
Невозможно преобразование типов.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Console.WriteLine("Класс A:"+test.b);
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
В данной программе возникает ошибка на этапе компиляции, так как невозможно автоматическое преобразование по отношению к переменным ссылочного типа (оно распространяется только на переменные обычных типов). Отсюда можно сделать вывод, что ссылочная переменная одного класса не может ссылаться на объект другого класса. Однако из этого правила есть одно исключение. Ссылочной переменной базового класса можно присвоить ссылку на объект дочернего класса, в этом случае через неё можно будет обратиться к тем членам производного, которые были унаследованы из базового. Например:
Код:
using System;
class Base{
public int x;
public int y;
}
class Child:Base{
public int z;
}
class Samples
{
static void Main()
{
try{
Base rf;
Child rf2;
Child obj = new Child();
obj.x = 0;
obj.y = 1;
obj.z = 2;
rf2 = obj;
Console.WriteLine(obj.x+" "+obj.y+" "+obj.z);
Console.WriteLine(rf2.x+" "+rf2.y+" "+rf2.z);
rf = obj; // Теперь можно так как Child наследуется от Base !!!
Console.WriteLine(rf.x+" "+rf.y);
// Ошибка компиляции так как z определен в Child,
а rf - это ссылка на базовый класс
// rf.z = 4;
Base obj2 = new Base();
// Ошибка компиляции так как rf2 - это ссылка на дочерний класс.
// И в неё нельзя присвоить ссылку на объект базового класса.
// rf2 = obj2;
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}
Запрет наследования
Иногда при разработке класса нужно запретить возможность наследования от него. Для этого используется ключевое слово sealed, которое указывается при объявлении класса. Данный механизм может понадобиться при разработке какого-то служебного класса. Например, уже известный вам класс Console библиотеки .NET Framework объявлен с использованием sealed. Рассмотрим программу, демонстрирующую этот принцип:
Код:
using System;
// От класса Childless невозможно наследоваться
sealed class Childless{
public int x;
public int y;
}
// Ошибка компиляции !!! Наследование невозможно Childless обьявлен
// с помощью ключевого слова sealed
class Child:Childless{
public int z;
}
class Samples
{
static void Main()
{
try{
Child obj = new Child();
}
catch(Exception e){
Console.WriteLine(e.Message);
}
Console.Read();
}
}