С интерфейсами и наследованием связаны две распространенные проблемы. Первая, проиллюстрированная приведенным ниже кодом, связана с созданием производного класса, содержащего метод, чье имя идентично имени метода интерфейса, который должен быть реализован классом:
using System;
public class Control {
public void SerializeO
<
Console.WriteLine("Control.Serialize called");
> >
public interface IDataBound {
void SerializeO;
}
public class EditBox : Control, IDataBound
{
>
class InterfacelnhlApp
{
public static void Main()
{
EditBox edit = new EditBoxQ; edit. SerializeO; } }
Как вы знаете, чтобы реализовать интерфейс, нужно предоставить определение каждого члена в определении интерфейса. Однако в предыдущем примере мы этого не сделали, а код все равно компилируется! Причина в том, что компилятор С# ищет метод
Serialize,
реализованный в классе
EditBox,
и находит его. Однако компилятор неверно определяет, что это реализованный метод. Метод
Serialize,
найденный компилятором, унаследован от класса
Control
методом
Serialize,
но не является настоящей реализацией метода
IDataBound.Serialize.
Поэтому, хоть он и компилируется, этот код не будет функционировать, как задумано, в чем мы убедимся позже.
Теперь внесем дополнительную интригу. Следующий код сначала проверяет оператором
as,
реализован ли интерфейс, затем пытается вызвать реализованный метод
Serialize.
Этот код компилируется и работает. Однако, как мы знаем, в классе
EditBox
метод
Serialize
на самом деле не реализован из-за наследования
IDataBound.
В
EditBox
уже есть метод
Serialize
(унаследованный) от класса
Control.
Это значит, что клиент, по всей вероятности, не получит ожидаемых результатов.
using System;
public class Control {
public void SerializeO
{
Console.WriteLine("Control.Serialize called");
} }
public interface IDataBound {
void SerializeO; }
public class EditBox : Control, IDataBound
{
}
class InterfaceInh2App {
public static void MainО
<
EditBox edit = new EditBoxO;
IDataBound bound = edit as IDataBound;
if (bound != null)
{
Console.WriteLine("IDataBound is supported...");
bound. SerializeO; }
else {
Console.WriteLineC'IDataBound is NOT supported...");
>
> >
Возникновение другой потенциальной проблемы ожидается, когда у производного класса есть метод с тем же именем, что и у реализации метода интерфейса из базового класса. Давайте рассмотрим этот код:
using System;
interface ITest
{
void Foo();
}
// В классе Base реализован интерфейс ITest. class Base : ITest
<
public void Foo()
{
Console.WriteLine("Base.Foo (ITest implementation)");
> }
class HyDerived : Base
{
public new void Foo()
{
Console.WriteLine("MyDerived.Foo");
} }
public class InterfacelnhSApp
{
public static void MainQ
{
MyDe rived myDe rived = new MyDerivedO;
myDerived.Foo();
ITest test = (ITest)myDerived; test.FooQ; }
}
В результате выполнения этот код выводит информацию:
MyDerived.Foo
Base.Foo (ITest implementation)
В этой ситуации в классе
Base
реализован интерфейс
ITest
и его метод
Foo.
Однако производный от
Base
класс
My Derived
реализует для этого класса новый метод
Foo.
Какой из методов
Foo
будет вызван? Это зависит от имеющейся у вас ссылки. Если есть ссылка на объект
My Derived,
вызывается его метод
Foo.
Это так, даже несмотря на то, что у объекта
myDerived
есть унаследованная реализация
ITest.Foo:
в период выполнения будет исполнен
MyDerived.Foo,
так как ключевым словом
new
задана подмена унаследованного метода.
Однако когда вы явно выполняете приведение объекта
myDerived
к интерфейсу
ITest,
компилятор разрешает реализацию интерфейса. У класса
MyDerived
есть метод с тем же именем, но это не тот метод, что ищет компилятор. Когда вы приводите объект к интерфейсу, компилятор проходит по дереву наследования, пока не найдет класс, содержащий в своем базовом списке интерфейс. Именно поэтому в результате выполнения последних двух строк кода метода
Main
вызывается метод
Foo,
реализованный в
ITest.
Хочется надеяться, что некоторые из этих потенциальных ловушек, включая конфликты имен и наследование интерфейсов, убедили вас следовать моей настоятельной рекомендации: всегда приводите объекты к интерфейсу, член которого вы пытаетесь использовать.