Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Другие элементы меню 16 страница




Уже достаточно давно разработан эффективный механизм реализации динамического связывания. Еще на этапе компиляции подготавливается так называемая таблица виртуальных методов, содержащая адреса виртуальных методов. Связывание объекта xk с принадлежащим ему методом Mk производится выбором соответствующего элемента из этой таблицы и выполняется ненамного сложнее, чем получение по индексу соответствующего элемента массива.

В языке C# принята следующая стратегия связывания. По умолчанию предполагается статическое связывание. Для того чтобы выполнялось динамическое связывание, метод родительского класса должен снабжаться модификатором virtual или abstract, а его потомки должны иметь модификатор override.

Три механизма, обеспечивающие полиморфизм

Под полиморфизмом в ООП понимают способность одного и того же программного текста x.M выполняться по-разному, в зависимости от того, с каким объектом связана сущность x. Полиморфизм гарантирует, что вызываемый метод M будет принадлежать классу объекта, связанному с сущностью x. В основе полиморфизма, характерного для семейства классов, лежат три механизма:

Одностороннее присваивание объектов внутри семейства классов. Сущность, базовым классом которой является класс предка можно связать с объектом любого из потомков. Другими словами, для введенной нами последовательности объектов xk присваивание xi = xj допустимо для всех j >=i.

Переопределение потомком метода наследованного от родителя. Благодаря переопределению в семействе классов существует совокупность полиморфных методов с одним именем и сигнатурой.

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

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

Вернемся к нашему примеру с классами Found, Derived, ChildDerived. Напомню, в родительском классе определен виртуальный метод VirtMethod и переопределен виртуальный метод ToString родительского класса object. Потомок класса Found – класс Derived переопределяет эти методы:

public override void VirtMethod()

{

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

}

public override string ToString()

{

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

name, credit, debet));

}

Потомок класса Derived – класс ChildDerived не создает новых полей. Поэтому он использует во многом методы родителя. Его конструктор состоит из вызова конструктора родителя:

public ChildDerived(string name, int cred, int deb): base (name,cred, deb)

{}

Нет и переопределения метода Tostring, поскольку используется реализация родителя. А вот метод VirtMethod переопределяется:

public override void VirtMethod()

{

Console.WriteLine("внук: " + this.ToString());

}

В классе Found определены два не виртуальных метода NonVirtmethod и Work, наследуемые потомками Derived и ChildDerived без всяких переопределений. Вы ошибаетесь, если думаете, что работа этих методов полностью определяется базовым классом Found. Полиморфизм делает работу этих методов куда более интересной. Давайте рассмотрим в деталях работу метода Work:

public void Work()

{

VirtMethod();

NonVirtMethod();

Analysis();

}

При компиляции метода Work будет обнаружено, что вызываемый метод VirtMethod является виртуальным, поэтому для него будет применяться динамическое связывание. Это означает, что вопрос о вызове метода откладывается до момента, когда метод Work будет вызван объектом, связанным с x. Объект может принадлежать как классу Found, так и классам Derived и ChildDerived, в зависимости от класса объекта и будет вызван метод этого класса.

Для не виртуальных методов NonVirtMethod и Analysis будет применено статическое связывание, так что Work всегда будет вызывать методы, принадлежащие классу Found. Однако и здесь не все просто. Метод NonVirtMethod

public void NonVirtMethod()

{

Console.WriteLine ("Мать: "+ this. ToString());

}

в процессе своей работы вызывает виртуальный метод ToString. Опять-таки, для метода ToString будет применяться динамическое связывание и в момент выполнения будет вызываться метод класса объекта.

Что же касается метода Analysis, определенного в каждом классе, то всегда в процессе работы Work будет вызываться только родительский метод анализа из-за стратегии статического связывания.

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

Класс Found, создающий метод Work говорит примерно следующее. Я предоставляю этот метод своим потомкам. Потомок, вызвавший этот метод, должен иметь VirtMethod, выполняющий специфическую для потомка часть работы, конечно, потомок может воспользоваться и моей реализацией, но допустима и его собственная реализация. Затем часть работы, выполняю я сам, но выдача информации об объекте определяется самим объектом. Заключительную часть работы, связанную с анализом, я потомкам не доверяю и делаю ее сам.

Пример работы с полиморфным семейством классов

Классы семейства с полиморфными методами уже созданы. Давайте теперь в клиентском классе Testing напишем метод, создающий объекты наших классов и вызывающий методы классов для объектов семейства:

public void TestFoundDerivedReal()

{

Found bs = new Found ("father", 777);

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

bs.VirtMethod();

bs.NonVirtMethod();

bs.Analysis();

bs.Work();

Derived der = new Derived("child", 888, 555);

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

der.DerivedMethod();

der.VirtMethod();

der.NonVirtMethod();

der.Analysis();

der.Work();

ChildDerived chider = new ChildDerived("grandchild", 999, 444);

Console.WriteLine("Объект chider вызывает методы ChildDerived");

chider.VirtMethod();

chider.NonVirtMethod();

chider.Analysis(5);

chider.Work();

}

Вот как выглядят результаты работы этого метода:

Рис. 18.2 Полиморфизм семейства классов

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

 

Абстрактные классы

С наследованием тесно связан еще один важный механизм проектирования семейства классов – механизм абстрактных классов. Начну с определений.

Класс называется абстрактным, если он имеет хотя бы один абстрактный метод.

Метод называется абстрактным, если при определении метода задана его сигнатура, но не задана реализация метода.

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

Абстрактные классы являются одним из важнейших инструментов объектно-ориентированного проектирования классов. К сожалению, я не буду входить в детали рассмотрения этой важной темы, и ограничусь лишь рассмотрением самой идеи применения абстрактного класса. В основе любого класса лежит абстракция данных. Абстрактный класс описывает эту абстракцию, не входя в детали реализации, ограничиваясь описанием тех операций, которые можно выполнять над данными класса. Так проектирование абстрактного класса Stack, описывающего стек, может состоять из рассмотрения основных операций над стеком, и не определять, как будет реализован стек – списком или массивом. Два потомка абстрактного класса –ArrayStack и ListStack могут быть уже конкретными классами, основанными на различных представлениях стека.

Вот описание полностью абстрактного класса Stack:

public abstract class Stack

{

public Stack()

{}

/// <summary>

/// втолкнуть элемент item в стек

/// </summary>

/// <param name="item"></param>

public abstract void put(int item);

/// <summary>

/// удалить элемент в вершине стека

/// </summary>

public abstract void remove();

/// <summary>

/// прочитать элемент в вершине стека

/// </summary>

public abstract int item();

/// <summary>

/// определить, пуст ли стек

/// </summary>

/// <returns></returns>

public abstract bool IsEmpty();

}

Описание класса содержит только сигнатуры методов класса и их спецификацию, заданную тэгами summary. Построим теперь одного из потомков этого класса, реализация которого основана на списковом представлении. Класс ListStack будет потомком абстрактного класса Stack и клиентом класса Linkable, задающего элементы списка. Класс Linkable выглядит совсем просто:

public class Linkable

{

public Linkable()

{

}

public int info;

public Linkable next;

}

В нем два поля и конструктор по умолчанию. Построим теперь класс ListStack:

public class ListStack: Stack

{

public ListStack()

{

top = new Linkable();

}

Linkable top;

/// <summary>

/// втолкнуть элемент item в стек

/// </summary>

/// <param name="item"></param>

public override void put(int item)

{

Linkable newitem = new Linkable();

newitem.info = item;

newitem.next = top;

top = newitem;

}

/// <summary>

/// удалить элемент в вершине стека

/// </summary>

public override void remove()

{

top = top.next;

}

/// <summary>

/// прочитать элемент в вершине стека

/// </summary>

public override int item()

{

return (top.info);

}

/// <summary>

/// определить, пуст ли стек

/// </summary>

/// <returns></returns>

public override bool IsEmpty()

{

return (top.next == null);

}

}

Класс имеет одно поле top класса Linkable и методы, наследованные от абстрактного класса Stack. Теперь, когда задано представление данных, нетрудно написать реализацию операций. Реализация операций традиционна для стеков и, надеюсь, не требует пояснений.

Приведу пример работы со стеком:

public void TestStack()

{

ListStack stack = new ListStack();

stack.put(7); stack.put(9);

Console.WriteLine(stack.item());

stack.remove(); Console.WriteLine(stack.item());

stack.put(11); stack.put(13);

Console.WriteLine(stack.item());

stack.remove(); Console.WriteLine(stack.item());

if (!stack.IsEmpty()) stack.remove();

Console.WriteLine(stack.item());

}

В результате работы этого теста будет напечатана следующая последовательность целых: 9, 7, 13, 11, 7.

Классы без потомков

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

Вариант 1

49. Для классов клиентов и поставщиков справедливы утверждения:

q у класса поставщика может быть много клиентов;

q у класса клиента может быть много поставщиков;

q поставщик может быть собственным поставщиком;

q клиент может быть собственным клиентом;

q отношение «клиент-поставщик» – задает отношение «имеет»;

q отношение «клиент-поставщик» – задает отношение «является».

50. Отметьте истинные высказывания:

q класс называется абстрактным, если он не вводит собственных полей данных;

q если в методах класса А вызываются методы класса В, то это означает, что класс А является клиентом класса В;

q у класса может быть несколько непосредственных родительских классов;

q у класса может быть только один непосредственный потомок;

q в проектах на C# контроль типов выполняется на этапе компиляции.

51. В родительском классе описан метод public virtual void M(int x) {}. Какие объявления в классе потомке вызовут ошибку на этапе компиляции?

q override void M(int x){}

q public override void M(int item){}

q public new void M(int x){}

q public virtual void M(int x){}

q public virtual void M(int x, int y){}

Вариант 2

53. Для классов родителей и потомков справедливы следующие утверждения:

q у родительского класса может несколько непосредственных потомков;

q у класса потомка может быть несколько непосредственных родительских классов;

q класс родитель может быть сам себе родитель;

q класс потомок может быть собственным потомком;

q потомок наследует все поля и методы родителя, за исключением закрытых (private);

54. Отметьте истинные высказывания:

q класс с модификатором sealed не может иметь потомков;

q класс с модификатором abstract не может иметь потомков с таким же модификатором;

q если не задан специальный модификатор, то по умолчанию применяется динамическое связывание;

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

55. В родительском классе описан метод public virtual void M(int x) {}. Какие объявления в классе потомке вызовут предупреждения на этапе компиляции?

q override void M(int x){}

q public override void M(int item){}

q public new void M(int x){}

q public virtual void M(int x){}

q public virtual void M(int x, int y){} 2

Вариант 3

50. Для понятия «полиморфизм» справедливы следующие утверждения:

q полиморфизм определяется для семейства классов, связанных отношением наследования;

q полиморфизм определяется для семейства классов, связанных отношением вложенности;

q полиморфизм означает, что в классе заданы перегруженные методы;

q реализация полиморфизма построена на динамическом связывании;

q реализация полиморфизма предполагает статический контроль типов.

51. Отметьте истинные высказывания:

q вызов конструктора приводит к вызову конструкторов всех предков класса;

q клиенту доступны все методы поставщика;

q наследнику доступны все методы предков;

q абстрактный класс может иметь полностью реализованный метод;

q объект наследника «является» объектом родителя.

52. В родительском классе описан метод public void M(int x) {}. Какие объявления в классе потомке вызовут ошибки или предупреждения на этапе компиляции?

q public override void M(int x) {}

q public new void M(int x) {}

q public new void M(int x, int y){}

q public void M(int x){}

q private new void M(int x){}

 

Лекция 19 Интерфейсы. Множественное наследование

Интерфейсы как частный случай класса. Множественное наследование. Проблемы. Множественное наследование интерфейсов. Встроенные интерфейсы. Интерфейсы IComparable, IClonable, ISerializable. Поверхностное и глубокое клонирование и сериализация. Сохранение и обмен данными.

Интерфейсы

Слово «интерфейс» многозначное и в разных контекстах оно имеет различный смысл. В данной лекции речь идет о понятии интерфейса, стоящем за ключевым словом interface. В таком понимании интерфейс – это частный случай класса. Интерфейс представляет собой полностью абстрактный класс, все методы которого абстрактны. От абстрактного класса интерфейс отличается некоторыми деталями в синтаксисе и поведении. Синтаксическое отличие состоит в том, что методы интерфейса объявляются без указания модификатора доступа. Отличие в поведении заключается в более жестких требованиях к потомкам. Класс, наследующий интерфейс, обязан полностью реализовать все методы интерфейса. В этом отличие от класса, наследующего абстрактный класс, где потомок может реализовать лишь некоторые методы родительского абстрактного класса, оставаясь абстрактным классом. Но конечно не ради этих отличий были введены интерфейсы в язык C#. У них значительно более важная роль.

Введение в язык частных случаев усложняет язык и свидетельствует о некоторых изъянах в языке, для преодоления недостатков которых и вводятся частные случаи. Например, введение структур в язык C# позволило определять классы как развернутые типы. Конечно, проще было бы ввести в объявление класса соответствующий модификатор, позволяющий любой класс объявлять развернутым. Но этого сделано не было, а, следуя традиции языка С++, были введены структуры как частный случай классов.

Подробнее о развернутых и ссылочных типах см. лекцию 17

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

Отметим одно важное назначение интерфейсов. Интерфейс позволяет описывать некоторые желательные свойства, которыми могут обладать объекты разных классов. В библиотеке FCL имеется большое число подобных интерфейсов, с некоторыми из них мы познакомимся в этой лекции. Все классы, допускающие сравнение своих объектов, обычно наследуют интерфейс IComparable, реализация которого позволяет сравнивать объекты не только на равенство, но и на «больше», «меньше».

Две стратегии реализации интерфейса

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

public interface IProps

{

void Prop1(string s);

void Prop2 (string name, int val);

}

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

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

public class Clain:IProps

{

public Clain() {}

public void Prop1(string s)

{

Console.WriteLine(s);

}

public void Prop2(string name, int val)

{

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

}

}// Clain

Класс реализует методы интерфейса, делая их открытыми для клиентов класса и наследников. Другая стратегия реализации интерфейса состоит в том, чтобы все или некоторые методы интерфейса сделать закрытыми. Для реализации этой стратегии класс, наследующий интерфейс, объявляет методы без модификатора доступа, что по умолчанию соответствует модификатору 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);

}

}// class ClainP

Класс ClainP реализовал методы интерфейса IProps, но сделал их закрытыми и недоступными для вызова клиентов и наследников класса. Как же получить доступ к закрытым методам? Есть два способа решения этой проблемы:

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

Кастинг. Создается объект интерфейсного класса 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 нельзя. Тем не менее, можно объявить объект интерфейсного класса и связать его с настоящим объектом путем приведения (кастинга) объекта наследника к классу интерфейса. Это преобразование задается явно. Имея объект, можно вызывать методы интерфейса, даже если они закрыты в классе, для интерфейсных объектов они являются открытыми. Приведу соответствующий пример, в котором идет работа как с объектами классов Clain, ClainP, так и с объектами интерфейсного класса IProps:

public void TestClainIProps()

{

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

Clain clain = new Clain();

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

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

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

IProps ip = (IProps)clain;

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

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

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

ClainP clainp = new ClainP();

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

clainp.MyProp2("Владимир", 44);

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

IProps ipp = (IProps)clainp;

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

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

}

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

Рис. 19.1 Наследование интерфейса. Две стратегии

Проблемы множественного наследования

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

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

Проблема коллизии имен возникает, когда два или более интерфейса имеют методы с одинаковыми именами и сигнатурой. Сразу же заметим, что если имена методов совпадают, но сигнатуры разные, то это не приводит к конфликтам – при реализации у класса наследника просто появляются перегруженные методы. Но что следует делать классу наследнику в тех случаях, когда сигнатуры методов совпадают? И здесь возможны две стратегии – склеивание методов и переименование.

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

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

Итак, коллизия имен при множественном наследовании интерфейсов, хотя и возможна, но легко разрешается. Разработчик класса может выбрать любую из двух возможных стратегий, наиболее подходящую для данного конкретного случая. Приведу пример двух интерфейсов, имеющих методы с одинаковой сигнатурой, и класса – наследника этих интерфейсов, применяющего разные стратегии реализации для конфликтующих методов.

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

{

/// <summary>

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

/// </summary>

/// <param name="s"></param>

public void Prop1 (string s)

{

Console.WriteLine(s);

}

/// <summary>

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

/// </summary>

/// <param name="s"></param>

/// <param name="x"></param>

public void Prop2(string s, int x)

{

Console.WriteLine(s + "; " + x);

}

public void Prop2 (int x)

{

Console.WriteLine(x);

}

/// <summary>

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

/// </summary>

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();

}

}

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




Поделиться с друзьями:


Дата добавления: 2014-12-25; Просмотров: 541; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.214 сек.