public interface IDraw{
void Draw();
}
public interface IDrawToPrinter{
void Draw();
}
Если вы захотите построить класс с именем SuperImage (суперизображение), поддерживающий базовую визуализацию (IDraw), 3D-визуализацию (IDraw3D), а также сервис печати (IDrawToPrinter), то единственным способом обеспечить уникальную реализацию для каждого метода будет использование явной реализации интерфейса.
// Не выводится из Shape, но вводит конфликт имен.
public class SuperImage: IDraw, IDrawToPrinter, IDraw3D {
void IDraw.Draw(){/* Логика базовой визуализации. */}
void IDrawToPrinter.Draw(){/* Логика печати. */}
void IDraw3D.Draw(){/* Логика 3D-визуализации. */}
}
Исходный код. Проект CustomInterface размешен в подкаталоге, соответствующем главе 7.
Построение иерархии интерфейсов
Продолжим наше обсуждение вопросов создания пользовательских интерфейсов и рассмотрим тему иерархии интерфейсов. Вы знаете, что класс может выступать в роли базового класса для других классов (которые, в свою очередь, тоже могут быть базовыми классами для других классов), но точно так же можно строить отношения наследования и среди интерфейсов. Как и следует ожидать, интерфейс на вершине иерархии определяет общее поведение, а интерфейсы, находящиеся на более низких уровнях, "уточняют" это поведение. Для примера рассмотрите следующую иерархию интерфейсов.
// Базовый интерфейс.
public interface IDrawable { void Draw(); }
public interface IPrintable: IDrawable{ void Print(); }
public interface IMetaFileRender: IPrintable{ void Render(); }
Соответствующая цепочка наследования показана на рис. 7.5.
Рис. 7.5. Иерархия интерфейсов
Теперь, если некоторый класс должен поддерживать все варианты поведения, заданные в рамках этой иерархии интерфейсов, то этот класс должен выводиться из интерфейса, лежащего в основе иерархии (в данном случае это IMetaFileRender). Все методы, определенные базовым интерфейсом (или интерфейсами), автоматически переносятся в определение. Например:
// Этот класс поддерживает IDrawable, IPrintable и IMetaFileRender.
public class SuperImage: IMetaFileRender {
public void Draw() { Console.WriteLine("Базовая логика визуализации."); }
public void Print() { Console.WriteLine("Вывод на принтер."); }
public void Render() { Console WriteLine("Вывод в метафайл."); }
}
Вот пример вывода каждого интерфейса из экземпляра SuperImage.
// Использование интерфейсов.
static void Main(string[] args) {
SuperImage si = new SuperImage();
// Получение IDrawable.
IDrawable itfDraw = (IDrawable)si;
itfDraw.Draw();
// Получение IMetaFileRender, который использует все методы,
// определенные выше по цепочке интерфейсов.
if (itfDraw is IMetaFileRender) {
IMetaFileRender itfMF = (IMetaFileRender)itfDraw;
itfMF.Render();
itfMF.Print() ;
}
Console.ReadLine();
}
Интерфейсы с множеством базовых интерфейсов
При построении иерархии интерфейсов вполне допустимо создавать интерфейсы, которые оказываются производными от нескольких базовых интерфейсов. Однако напомним, что нельзя строить классы, которые будут производными от нескольких базовых классов. Для примера предположим, что вы строите набор интерфейсов, моделирующих поведение автомобиля.
public interface ICar { void Drive(); }
public interface IUnderwaterCar { void Dive(); }
// Здесь интерфейс имеет ДВА базовых интерфейса.
public interface IJamesBondCar: ICar, IUnderwaterCar{ void TurboBoost(); }
На рис. 7.6 показана соответствующая цепочка интерфейсов.
Рис. 7.6. Общая система типов (CTS) допускает множественное наследование интерфейсных типов
При построении класса, реализующего IJamesBondCar (машина Джеймса Бонда), вы должны реализовать TurboBoost(), Dive() и Drive().
public class JamesBondCar: IJamesBondCar {
public void Drive() { Console.WriteLine("Ускорение…"); }
public void Dive() { Console.WriteLine("Погружение…"); }
public void TurboBoost() { Console.WriteLine{"Взлет!"); }
}
Этот специализированный автомобиль можно использовать так, как и ожидается.
static void Main(string[] args) {
…
JamesBоndCar j = new JamesBondCar();
j.Drive();
j.TurboBoost();
j.Dive();
}
Читать дальше