Далее вы можете проверить, поддерживает ли элемент в массиве
Shape
новый интерфейс, и если поддерживает, то передать его методу
DrawIn3D
на обработку:
Console.WriteLine("***** Fun with Interfaces *****\n");
Shape[] myShapes = { new Hexagon, new Circle,
new Triangle("Joe"), new Circle("JoJo") } ;
for(int i = 0; i < myShapes.Length; i++)
{
// Can I draw you in 3D?
if (myShapes[i] is IDraw3D s)
{
DrawIn3D(s);
}
}
Ниже
представлен вывод, полученный из модифицированной версии приложения. Обратите внимание, что в трехмерном виде отображается только объект
Hexagon
, т.к. все остальные члены массива
Shape
не реализуют интерфейс
IDraw3D
:
***** Fun with Interfaces *****
...
– > Drawing IDraw3D compatible type
Drawing Hexagon in 3D!
Использование интерфейсов в качестве возвращаемых значений
Интерфейсы могут также применяться в качестве типов возвращаемых значений методов. Например, можно было бы написать метод, который получает массив объектов
Shape
и возвращает ссылку на первый элемент, поддерживающий
// В целях безопасности использовать null-условную операцию.
Console.WriteLine("The item has {0} points",
firstPointyItem?.Points);
Массивы интерфейсных типов
Вспомните, что один интерфейс может быть реализован множеством типов, даже если они не находятся внутри той же самой иерархии классов и не имеют общего родительского класса помимо
System.Object
. Это позволяет формировать очень мощные программные конструкции.
Например, пусть в текущем проекте разработаны три новых класса: два класса (
Knife
(нож) и
Fork
(вилка)) моделируют кухонные приборы, а третий (
PitchFork
(вилы)) — садовый инструмент. Ниже показан соответствующий код, а на рис. 8.3 — обновленная диаграмма классов.
// Fork.cs
namespace CustomInterfaces
{
class Fork : IPointy
{
public byte Points => 4;
}
}
// PitchFork.cs
namespace CustomInterfaces
{
class PitchFork : IPointy
{
public byte Points => 3;
}
}
// Knife.cs.cs
namespace CustomInterfaces
{
class Knife : IPointy
{
public byte Points => 1;
}
}
После определения типов
PitchFork
,
Fork
и
Knife
можно определить массив объектов, совместимых с
IPointy
. Поскольку все элементы поддерживают один и тот же интерфейс, допускается выполнять проход по массиву и интерпретировать каждый его элемент как объект, совместимый с
IPointy
, несмотря на разнородность иерархий классов:
...
// Этот массив может содержать только типы,
// которые реализуют интерфейс IPointy.
IPointy[] myPointyObjects = {new Hexagon, new Knife,
new Triangle, new Fork, new PitchFork};
foreach(IPointy i in myPointyObjects)
{
Console.WriteLine("Object has {0} points.", i.Points);
}
Console.ReadLine;
Просто чтобы подчеркнуть важность продемонстрированного примера, запомните, что массив заданного интерфейсного типа может содержать элементы любых классов или структур, реализующих этот интерфейс.
Автоматическая реализация интерфейсов
Хотя программирование на основе интерфейсов является мощным приемом, реализация интерфейсов может быть сопряжена с довольно большим объемом клавиатурного ввода. Учитывая, что интерфейсы являются именованными наборами абстрактных членов, для каждого метода интерфейса в каждом типе, который поддерживает данное поведение, потребуется вводить определение и реализацию. Следовательно, если вы хотите поддерживать интерфейс, который определяет пять методов и три свойства, тогда придется принять во внимание все восемь членов (иначе возникнут ошибки на этапе компиляции).