interface IRegularPointy : IPointy
{
int SideLength { get; set; }
int NumberOfSides { get; set; }
int Perimeter => SideLength * NumberOfSides;
// Статические члены также разрешены в версии C# 8
static string ExampleProperty { get; set; }
static IRegularPointy() => ExampleProperty = "Foo";
}
Статические конструкторы не должны иметь параметры и могут получать доступ только к статическим свойствам и методам. Для обращения к статическому свойству интерфейса добавьте к операторам верхнего уровня следующий код:
Console.WriteLine($"Example property: {IRegularPointy.ExampleProperty}");
IRegularPointy.ExampleProperty = "Updated";
Console.WriteLine($"Example property: {IRegularPointy.ExampleProperty}");
Обратите внимание, что к статическому свойству необходимо обращаться через интерфейс, а не переменную экземпляра.
Использование интерфейсов в качестве параметров
Учитывая, что интерфейсы являются допустимыми типами, можно строить методы, которые принимают интерфейсы в качестве параметров, как было проиллюстрировано на примере метода
CloneMe()
ранее в главе. Предположим, что вы определили в текущем примере еще один интерфейс по имени
IDraw3D
:
namespace CustomInterfaces
{
// Моделирует способность визуализации типа в трехмерном виде.
public interface IDraw3D
{
void Draw3D();
}
}
Далее сконфигурируйте две из трех фигур (
Circle
и
Hexagon
) с целью поддержки нового поведения:
// Circle поддерживает IDraw3D.
class ThreeDCircle : Circle, IDraw3D
{
...
public void Draw3D()
=> Console.WriteLine("Drawing Circle in 3D!"); }
}
// Hexagon поддерживает IPointy и IDraw3D.
class Hexagon : Shape, IPointy, IDraw3D
{
...
public void Draw3D()
=> Console.WriteLine("Drawing Hexagon in 3D!");
}
На рис. 8.2 показана обновленная диаграмма классов в Visual Studio.
Теперь если вы определите метод, принимающий интерфейс
IDraw3D
в качестве параметра, тогда ему можно будет передавать по существу любой объект, реализующий
IDraw3D
. Попытка передачи типа, не поддерживающего необходимый интерфейс, приводит ошибке на этапе компиляции. Взгляните на следующий метод, определенный в классе
Program
:
// Будет рисовать любую фигуру, поддерживающую IDraw3D.
static void DrawIn3D(IDraw3D itf3d)
{
Console.WriteLine("-> Drawing IDraw3D compatible type");
itf3d.Draw3D();
}
Далее вы можете проверить, поддерживает ли элемент в массиве
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
и возвращает ссылку на первый элемент, поддерживающий
IPointy
:
// Этот метод возвращает первый объект в массиве,
// который реализует интерфейс IPointy.
static IPointy FindFirstPointyShape(Shape[] shapes)
{
foreach (Shape s in shapes)
{
if (s is IPointy ip)