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

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

Явные реализации

При реализации члена интерфейса имеется возможность указать его имя полно­стью вместе с именем самого интерфейса. В этом случае получается явная реализация члена интерфейса, или просто явная реализация. Так, если объявлен интерфейс IMylF

Листинг 12.10

interface IMyIF

{

int MyMeth(int x) ;

}

то следующая его реализация считается вполне допустимой:

Листинг 12.11

class MyClass : IMyIF

{

int IMyIF.MyMeth(int x)

{

return x / 3;

}

}

Как видите, при реализации члена MyMeth() интерфейса IMyIF указывается его полное имя, включающее в себя имя его интерфейса.

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

В приведенном ниже примере программы демонстрируется интерфейс IEven, в котором объявляются два метода: IsEven() и IsOdd(). В первом из них определяет­ся четность числа, а во втором - его нечетность. Интерфейс IEven затем реализуется в классе MyClass. При этом метод IsOdd() реализуется явно.

Листинг 12.12

// Реализовать член интерфейса явно.

 

using System;

 

interface IEven

{

bool IsOdd(int x);

bool IsEven(int x);

}

 

class MyClass : IEven

{

// Явная реализация. Обратите внимание на то,

// что этот член является закрытым по умолчанию.

bool IEven.IsOdd(int x)

{

if((x%2) != 0) return true;

else return false;

}

 

// Обычная реализация.

public bool IsEven(int x)

{

IEven o = this; // Интерфейсная ссылка на вызывающий объект.

 

return !o.IsOdd(x);

}

}

 

class Demo {

static void Main() {

MyClass ob = new MyClass();

bool result;

 

result = ob.IsEven(4);

if(result) Console.WriteLine("4 – четное число.");

 

// result = ob.IsOdd(4); // Ошибка, член IsOdd интерфейса

// IEven недоступен.

 

// Следующий код написан верно, поскольку в нем сначала

// интерфейсная ссылка типа IEven на объект класса MyClass,

// а затем по этой ссылке вызывается метод IsOdd().

IEven iRef = (IEven) ob;

result = iRef.IsOdd(3);

if(result) Console.WriteLine("3 – нечетное число.");

 

}

}

В приведенном выше примере метод IsOdd() реализуется явно, а значит, он недо­ступен как открытый член класса MyClass. Напротив, он доступен только по интер­фейсной ссылке. Именно поэтому он вызывается посредством переменной о ссылоч­ного типа IEven в реализации метода IsEven().

Ниже приведен пример программы, в которой реализуются два интерфейса, при­чем в обоих интерфейсах объявляется метод Meth(). Благодаря явной реализации ис­ключается неоднозначность, характерная для подобной ситуации.

Листинг 12.13

// Использовать явную реализацию для устранения неоднозначности.

 

using System;

 

interface IMyIF_A

{

int Meth(int x);

}

 

interface IMyIF_B

{

int Meth(int x);

}

 

// Оба интерфейса реализуются в классе MyClass.

class MyClass : IMyIF_A, IMyIF_B

{

// Реализовать оба метода Meth()явно.

int IMyIF_A.Meth(int x)

{

return x + x;

}

int IMyIF_B.Meth(int x)

{

return x * x;

}

 

// Вызвать метод Meth() по интерфейсной ссылке.

public int MethA(int x)

{

IMyIF_A a_ob;

a_ob = this;

return a_ob.Meth(x); // вызов интерфейсного метода IMyIF_A

}

 

public int MethB(int x)

{

IMyIF_B b_ob;

b_ob = this;

return b_ob.Meth(x); // вызов интерфейсного метода IMyIF_B

}

}

 

class FQIFNames

{

static void Main()

{

MyClass ob = new MyClass();

 

Console.Write("Вызов метода IMyIF_A.Meth(): ");

Console.WriteLine(ob.MethA(3));

 

Console.Write("Вызов метода IMyIF_B.Meth(): ");

Console.WriteLine(ob.MethB(3));

}

}

Вот к какому результату приводит выполнение этой программы.

Вызов метода IMylF A.Meth(): 6

Вызов метода IMyIF_B.Meth(): 9

Анализируя приведенный выше пример программы, обратим прежде всего вни­мание на одинаковую сигнатуру метода Meth () в обоих интерфейсах, IMyIF_A и IMyIF_B. Когда оба этих интерфейса реализуются в классе MyClass, для каждого из них в отдельности это делается явно, т.е. с указанием полного имени метода Meth(). А поскольку явно реализованный метод может вызываться только по интерфейсной ссылке, то в классе MyClass создаются две такие ссылки: одна - для интерфейса IMyIF_А, а другая - для интерфейса IMyIF_В. Именно по этим ссылкам происходит обращение к объектам данного класса с целью вызвать методы соответствующих интерфейсов, благодаря чему и устраняется неоднозначность.