Console.WriteLine($"Time to draw: {iAdvDraw.TimeToDraw}");
}
Console.ReadLine;
Этот
код не только скомпилируется, но и выведет значение
5
при вызове метода
TimeToDraw
. Дело в том, что стандартные реализации попадают в производные интерфейсы автоматически. Приведение
BitMapImage
к интерфейсу
IAdvancedDraw
обеспечивает доступ к методу
TimeToDraw
, хотя экземпляр
BitMapImage
не имеет доступа к стандартной реализации. Чтобы удостовериться в этом, введите следующий код, который вызовет ошибку на этапе компиляции:
// Этот код не скомпилируется
myBitmap.TimeToDraw;
Если в нижерасположенном интерфейсе желательно предоставить собственную стандартную реализацию, тогда потребуется скрыть вышерасположенную реализацию. Например, если вычерчивание в методе
TimeToDraw
из
IAdvancedDraw
занимает 15 единиц времени, то модифицируйте определение интерфейса следующим образом:
public interface IAdvancedDraw : IDrawable
{
void DrawInBoundingBox(
int top, int left, int bottom, int right);
void DrawUpsideDown;
new int TimeToDraw => 15;
}
Разумеется, в классе
BitMapImage
также можно реализовать метод
TimeToDraw
. В отличие от метода
TimeToDraw
из
IAdvancedDraw
в классе необходимо только реализовать метод без его сокрытия.
public class BitmapImage : IAdvancedDraw
{
...
public int TimeToDraw => 12;
}
В случае приведения экземпляра
BitmapImage
к интерфейсу
IAdvancedDraw
или
IDrawable
метод на экземпляре по-прежнему выполняется. Добавьте к операторам верхнего уровня показанный далее код:
Console.WriteLine($"Time to draw: {myBitmap.TimeToDraw}");
Console.WriteLine($"Time to draw: {((IDrawable) myBitmap).TimeToDraw}");
Console.WriteLine($"Time to draw: {((IAdvancedDraw) myBitmap).TimeToDraw}");
Вот результаты:
***** Simple Interface Hierarchy *****
...
***** Calling Implemented TimeToDraw *****
Time to draw: 12
Time to draw: 12
Time to draw: 12
Множественное
наследование с помощью интерфейсных типов
В отличие от типов классов интерфейс может расширять множество базовых интерфейсов, что позволяет проектировать мощные и гибкие абстракции. Создайте новый проект консольного приложения по имени
MiInterfaceHierarchy
. Здесь имеется еще одна коллекция интерфейсов, которые моделируют разнообразные абстракции, связанные с визуализацией и фигурами. Обратите внимание, что интерфейс
IShape
расширяет и
IDrawable
, и
IPrintable
:
// IDrawable.cs
namespace MiInterfaceHierarchy
{
// Множественное наследование для интерфейсных типов - это нормально.
На рис. 8.6 показана текущая иерархия интерфейсов.
Главный вопрос: сколько методов должен реализовывать класс, поддерживающий
IShape
? Ответ: в зависимости от обстоятельств. Если вы хотите предоставить простую реализацию метода
Draw
, тогда вам необходимо реализовать только три члена, как иллюстрируется в следующем типе
Rectangle
:
using System;
namespace MiInterfaceHierarchy
{
class Rectangle : IShape
{
public int GetNumberOfSides => 4;
public void Draw => Console.WriteLine("Drawing...");
public void Print => Console.WriteLine("Printing...");
}
}
Если вы предпочитаете располагать специфическими реализациями для каждого метода
Draw
(что в данном случае имеет смысл), то конфликт имен можно устранить с использованием явной реализации интерфейсов, как делается в представленном далее типе