Добавление методов и изменение методов родителя

Потомок может добавлять новые собственные методы.

Класс - потомок может изменять наследуемые им методы. Если потомок создает метод с именем, совпадающим с именем метода предков, то возможны три ситуации:

· перегрузка метода. Она возникает, когда сигнатура создаваемого метода отличается от сигнатуры наследуемых методов предков;

· переопределение метода. Метод родителя в этом случае должен иметь модификатор virtual или abstract. При переопределении сохраняется сигнатура и модификаторы доступа наследуемого метода;

· сокрытие метода. Если родительский метод не является виртуальным или абстрактным, то потомок может создать новый метод с тем же именем и той же сигнатурой, скрыв родительский метод в данном контексте. При вызове метода предпочтение будет отдаваться методу потомка. Скрытый родительский метод всегда может быть вызван, если его уточнить ключевым словом base. Метод потомка следует сопровождать модификатором new, указывающим на новый метод.

Программа 4. Класс наследник с переопределенными методамиродителя

public class Derived:Found {

protected int debet;

public Derived() {}

public Derived(String name, int cred, int deb): base (name,cred){

debet = deb;

}

public void DerivedMethod(){ //новый метод потомка

Console.WriteLine("Это метод класса Derived");

}

new public void Analysis(){ //сокрытие метода родителя

base.Analysis();

Console.WriteLine("Сложный анализ");

}

public void Analysis(int level){ // перегрузка метода

base.Analysis();

Console.WriteLine("Анализ глубины {0}", level);

}

public override String ToString(){ //переопределение метода

return(String.Format("поля: name = {0}, credit = {1},debet ={2}",name, credit, debet));

}

// переопределение метода родителя

public override void VirtMethod() {

Console.WriteLine ("Сын: " + this.ToString() );

}

}

 

Интерфейсы. Интерфейс представляет собой полностью абстрактный класс, все методы которого абстрактны. Методы интерфейса объявляются без указания модификатора доступа (по умолчанию public). Класс, наследующий интерфейс, обязан реализовать все методы интерфейса.

В языке C# полного множественного наследования классов нет. Чтобы частично сгладить этот пробел, допускается множественное наследованиеинтерфейсов.

 

Две стратегии реализации интерфейса. Опишем некоторый интерфейс, задающий дополнительные свойства объектов класса:

public interface IProps{

void Prop1(string s);

void Prop2 (string name, int val);

}

· Класс, наследующий интерфейс и реализующий его методы, может реализовать их явно, объявляя соответствующие методы класса открытыми (public).

· Другая стратегия реализации состоит в том, чтобы некоторые методы интерфейса сделать закрытыми (private), уточнив имя метода именем интерфейса:

public class ClainP:IProps{

public ClainP(){ }

void IProps.Prop1(string s) {

Console.WriteLine(s);

}

void IProps.Prop2(string name, int val) {

Console.WriteLine("name = {0}, val ={1}", name, val);

}

}

Есть два способа получения доступа к закрытым методам:

· Обертывание. Создается открытый метод, являющийся оберткой закрытого метода.

· Кастинг. Создается объект интерфейсного класса IProps, полученный преобразованием (кастингом) объекта исходного класса ClainP. Этому объекту доступны закрытые методы интерфейса.

Вот пример обертывания закрытых методов в классе ClainP:

public void MyProp1(string s){

((IProps)this).Prop1(s);

}

public void MyProp2(string s, int x){

((IProps)this).Prop2(s, x);

}

Методы переименованы и получили другие имена, под которыми они и будут известны клиентам класса. В обертке для вызова закрытого метода пришлось использовать кастинг, приведя объект this к интерфейсному классу IProps.

 

Преобразование к классу интерфейса. Создать объект класса интерфейса обычным путем с использованием операции new нельзя, но можно объявить объект интерфейсного класса и связать его с настоящим объектом путем приведения (кастинга) объекта наследника к классу интерфейса.

public void TestClainIProps(){

Console.WriteLine("Объект класса ClainP вызывает открытые методы!");

ClainP clain = new Clain();

clain.Prop1(" свойство 1 объекта");

clain.Prop2("Владимир", 44);

Console.WriteLine("Объект класса IProps вызывает открытые методы!");

IProps ip = (IProps)clain;

ip.Prop1("интерфейс: свойство");

ip.Prop2 ("интерфейс: свойство",77);

}

 

Проблемы множественного наследования. При множественном наследовании интерфейсов так же возникают проблемы, но решение их становится проще. Рассмотрим две основные проблемы - коллизию имен и наследование от общего предка.

Коллизия имен

Проблема коллизии имен возникает, когда два или более интерфейса имеют методы с одинаковыми именами и сигнатурой. Здесь возможны две стратегии - склеивание методови переименование.

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

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

Пример двух интерфейсов, имеющих методы с одинаковой сигнатурой, и класса - наследника этих интерфейсов, применяющего разные стратегии реализации для конфликтующих методов:

Программа 5. Коллизии имен в интерфейсах

public interface IProps{

void Prop1(string s);

void Prop2 (string name, int val);

void Prop3();

}

public interface IPropsOne{

void Prop1(string s);

void Prop2 (int val);

void Prop3();

}

public class ClainTwo:IProps,IPropsOne {

// склеивание методов двух интерфейсов

public void Prop1 (string s) { Console.WriteLine(s); }

// перегрузка методов двух интерфейсов

public void Prop2(string s, int x) { Console.WriteLine(s + x); }

public void Prop2 (int x) { Console.WriteLine(x); }

// private реализация и переименование методов 2-х интерфейсов

void IProps.Prop3() {

Console.WriteLine("Метод 3 интерфейса 1");

}

void IPropsOne.Prop3() {

Console.WriteLine("Метод 3 интерфейса 2");

}

public void Prop3FromInterface1() { ((IProps)this).Prop3(); }

public void Prop3FromInterface2() { ((IPropsOne)this).Prop3(); }

}

public void TestTwoInterfaces(){

ClainTwo claintwo = new ClainTwo();

claintwo.Prop1("Склейка свойства двух интерфейсов");

claintwo.Prop2("перегрузка .: ",99);

claintwo.Prop2(9999);

claintwo.Prop3FromInterface1();

claintwo.Prop3FromInterface2();

Console.WriteLine("Интерфейсный объект вызывает методы 1-го интерфейса!");

IProps ip1 = (IProps)claintwo;

ip1.Prop1("интерфейс IProps: свойство 1");

ip1.Prop3();

Console.WriteLine("Интерфейсный объект вызывает методы 2-го интерфейса!");

IPropsOne ip2 = (IPropsOne)claintwo;

ip2.Prop1("интерфейс IPropsOne: свойство1");

ip2.Prop3();

}