Выбрать главу

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

Начнем наш пример с наследования интерфейсов:

public interface IParent

{

   void ParentMethod();

}

public interface ISonclass="underline" IParent {

   void SonlMethod();

}

public interface ISon2:IParent {

   void Son2Method();

}

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

public class Pars: ISon1, ISon2

{

    public void ParentMethod()

    {

       Console.WriteLine("Это метод родителя!");

    }

    public void Son1Method()

    {

       Console.WriteLine("Это метод старшего сына!");

    }

    public void Son2Method()

    {

        Console.WriteLine("Это метод младшего сына!");

    }

}//class Pars

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

public void TestlParsons ()

{

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

    Cli. Pars ct = new Cli.Pars();

    ct.ParentMethod();

    ct.Son1Method();

    ct.Son2Method();

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

    Cli.IParent ip = (IParent)ct;

    ip.ParentMethod();

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

    Cli.ISonl ipl = (ISon1)ct;

    ip1.ParentMethod ();

    ip1.SonlMethod();

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

    Cli.ISon2 ip2 = (ISon2)ct;

    ip2.ParentMethod ();

    ip2.Son2Method();

}

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

Рис. 19.3. Дублирующее наследование интерфейсов

Встроенные интерфейсы

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

Упорядоченность объектов и интерфейс IComparable

Часто, когда создается класс, желательно задать отношение порядка на его объектах. Такой класс следует объявить наследником интерфейса IComparable. Этот интерфейс имеет всего один метод CompareTo (object obj), возвращающий целочисленное значение, положительное, отрицательное или равное нулю, в зависимости от выполнения отношения "больше", "меньше" или "равно".

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

Давайте введем отношение порядка на классе Person, рассмотренном в лекции 16, сделав этот класс наследником интерфейса IComparable. Реализуем в этом классе метод интерфейса CompareTo:

public class Person: IComparable

{

    public int CompareTo(object pers)

    {

         const string s = "Сравниваемый объект не принадлежит классу Person";

         Person р = pers as Person;

         if (!p.Equals(null))

         return (fam.CompareTo(p.fam));

         throw new ArgumentException (s);

    }

    // другие компоненты класса

}

Поскольку аргумент метода должен иметь универсальный тип object, то перед выполнением сравнения его нужно привести к типу Person. Это приведение использует операцию as, позволяющую проверить корректность выполнения приведения.

При приведении типов часто используются операции is и as. Логическое выражение (obj is T) истинно, если объект obj имеет тип T. Оператор присваивания (obj = р as T;) присваивает объекту obj объект P, приведенный к типу T, если такое приведение возможно, иначе объекту присваивается значение null. Семантику as можно выразить следующим условным выражением: (P is T)? (T) P: (Т)null.