Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

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



Язык C# полностью поддерживает объектно-ориентированную концепцию наследования для классов. Чтобы указать, что один класс является наследником другого, используется следующий синтаксис:

class имя-класса-наследника: имя-класса-предка {тело-класса}

Наследование от двух и более классов в C# и CLR запрещено. Наследник обладает всеми полями, методами и свойствами предка, но элементы предка с модификатором private не доступны в наследнике. Конструкторы класса-предка не переносятся в класс-наследник. При наследовании также нельзя расширить область видимости класса, т.е. internal-класс может наследоваться от public-класса, но не наоборот.

Для объектов класса-наследника определено неявное преобразование к типу класса-предка. C# содержит две специальные операции, связанные с контролем типов при наследовании. Выражение x is T возвращает значение true, если тип объекта x – это T или наследник класса T. Выражение x as T возвращает объект, приведённый к типу T, если это возможно, и null в противном случае.

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

конструктор-наследника([параметры]): base([параметры_2])

Для конструкторов производного класса справедливо следующее замечание: в начале работы конструктор должен совершить вызов другого конструктора своего или базового класса. Если вызов конструктора базового класса отсутствует, компилятор автоматически подставляет в заголовок конструктора вызов base(). Если в базовом классе нет конструктора без параметров, происходит ошибка компиляции.

Для классов можно указать два модификатора, связанных с наследованием. Модификатор sealed определяет запечатанный класс, от которого запрещено наследование. Модификатор abstract определяет абстрактный класс, у которого обязательно должны быть наследники. Объект абстрактного класса создать нельзя, хотя статические элементы такого класса можно вызвать:

sealed class SealedClass { }

abstract class AbstractClass { }

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

public class Pet

{

public void Speak() { Console.WriteLine("I'm a pet"); }

}

public class Dog: Pet

{

public void Speak() { Console.WriteLine("I'm a dog"); }

}

При компиляции данного фрагмента будет получено предупреждающее сообщение о том, что метод Dog.Speak() закрывает метод базового класса Pet.Speak(). Чтобы подчеркнуть, что метод класса-наследника сознательно замещает метод базового класса, используется ключевое слово new:

public class Dog: Pet

{

public new void Speak() { Console.WriteLine("I'm a dog"); }

}

При замещении методов с изменением типов параметров метод базового класса вызывается только в том случае, если компилятор не может подобрать метод производного класса, выполняя неявное приведение типов:

public class A

{

public void Do(int x) { Console.WriteLine("A.Do()"); }

}

public class B: A

{

public void Do(double x) { Console.WriteLine("B.Do()"); }

}

B x = new B();

x.Do(3); // печатает "B.Do()"

Замещение методов класса не является полиморфным по умолчанию. Следующий фрагмент кода печатает две одинаковые строки:

Pet pet = new Pet();

Pet dog = new Dog(); // объект класса Dog присвоен объекту Pet

pet.Speak(); // печатает "I'm a pet"

dog.Speak(); // также печатает "I'm a pet"

Для организации полиморфного вызова применяется два модификатора: virtual указывается для метода базового класса, который мы хотим сделать полиморфным, override – для методов производных классов. Эти методы должны совпадать по имени, типу и сигнатуре с перекрываемым методом класса-предка.

public class Pet

{

public virtual void Speak() { Console.WriteLine("I'm a pet"); }

}

public class Dog: Pet

{

public override void Speak() { Console.WriteLine("I'm a dog"); }

}

Pet pet = new Pet();

Pet dog = new Dog();

pet.Speak(); // печатает "I'm a pet"

dog.Speak(); // печатает "I'm a dog"

При описании метода возможно совместное указание модификаторов new и virtual. Такой приём создаёт новую полиморфную цепочку замещения:

public class A

{

public virtual void Do() { Console.WriteLine("A.Do()"); }

}

public class B: A

{

public override void Do() { Console.WriteLine("B.Do()"); }

}

public class C: A

{

public new virtual void Do() { Console.WriteLine("C.Do()"); }

}

A[] x = {new A(), new B(), new C()};

x[0].Do(); // печатает "A.Do()"

x[1].Do(); // печатает "B.Do()"

x[2].Do(); // печатает "A.Do()"

Если на некоторой стадии построения иерархии классов требуется запретить дальнейшее переопределение виртуального метода в производных классах, этот метод помечается ключевым словом sealed:

public class Dog: Pet

{

public override sealed void Speak() { }

}

Для методов, объявленных в абстрактных классах, можно применить модификатор abstract, который говорит о том, что метод не реализуется в классе, не содержит тела и должен обязательно переопределяться в наследнике (в такой ситуации модификатор abstract эквивалентен модификатору virtual).

public abstract class AbstractClass

{

// реализации метода в классе нет

public abstract void AbstractMethod();

}

Отметим, что наряду с виртуальными методами в классе можно описать виртуальные свойства, индексаторы и события. Статические элементы класса не могут быть виртуальными.





Дата публикования: 2015-11-01; Прочитано: 844 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.009 с)...