Основы программирования на C#

         

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


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

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

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

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

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

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

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

public void TestCliTwoInterfaces() { Console.WriteLine("Объект ClainTwo вызывает методы двух интерфейсов!"); Cli.ClainTwo claintwo = new Cli.ClainTwo(); claintwo.Prop1("Склейка свойства двух интерфейсов"); claintwo.Prop2("перегрузка ::: ",99); claintwo.Prop2(9999); claintwo.Prop3FromInterface1(); claintwo.Prop3FromInterface2(); Console.WriteLine("Интерфейсный объект вызывает методы 1-го интерфейса!"); Cli.IProps ip1 = (Cli.IProps)claintwo; ip1.Prop1("интерфейс IProps: свойство 1"); ip1.Prop2("интерфейс 1 ", 88); ip1.Prop3(); Console.WriteLine("Интерфейсный объект вызывает методы 2-го интерфейса!"); Cli.IPropsOne ip2 = (Cli.IPropsOne)claintwo; ip2.Prop1("интерфейс IPropsOne: свойство1"); ip2.Prop2(7777); ip2.Prop3(); }

Результаты работы тестирующей процедуры показаны на рис. 19.2.


Рис. 19.2.  Решение проблемы коллизии имен


Содержание раздела