Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

public Square(string name) : base(name) { }

// Метод Draw поступает из базового класса Shape

public override void Draw

{

Console.WriteLine("Drawing a square");

}

// Это свойство поступает из интерфейса IPointy

public byte Points => 4;

// Это свойство поступает из интерфейса IRegularPointy.

public int SideLength { get; set; }

public int NumberOfSides { get; set; }

//
Обратите внимание, что свойство Perimeter не реализовано.

}

}

Здесь мы невольно попали в первую "ловушку", связанную с использованием стандартных реализаций интерфейсов. Свойство

Perimeter
, определенное в интерфейсе
IRegularPointy
, в классе
Square
не определено, что делает его недоступным экземпляру класса
Square
. Чтобы удостовериться в этом, создайте новый экземпляр класса
Square
и выведите на консоль соответствующие значения:

Console.WriteLine("\n***** Fun with Interfaces *****\n");

...

var sq = new Square("Boxy")

{NumberOfSides = 4, SideLength = 4};

sq.Draw;

// Следующий код не скомпилируется:

// Console.WriteLine($"{sq.PetName} has {sq.NumberOfSides} of length

{sq.SideLength} and a
perimeter of {sq.Perimeter}");

Взамен экземпляр

Square
потребуется явно привести к интерфейсу
IRegularPointy
(т.к. реализация находится именно там) и тогда можно будет получать доступ к свойству
Perimeter
. Модифицируйте код следующим образом:

Console.WriteLine($"{sq.PetName} has {sq.NumberOfSides} of length {sq.SideLength} and a

perimeter of {((IRegularPointy)sq).Perimeter}");

Один из способов обхода этой проблемы — всегда указывать интерфейс типа. Измените определение экземпляра

Square
, указав вместо типа
Square
тип
IRegularPointy
:

IRegularPointy sq = new Square("Boxy") {NumberOfSides = 4, SideLength = 4};

Проблема с таким подходом (в данном случае) связана с тем, что метод

Draw
и свойство
PetName
в интерфейсе не определены, а потому на этапе компиляции возникнут ошибки.

Хотя пример тривиален, он демонстрирует одну из проблем, касающихся стандартных реализаций. Прежде чем задействовать это средство в своем коде, обязательно оцените последствия того, что вызывающему коду должно быть известно, где находятся реализации.

Статические конструкторы и члены (нововведение в версии 8.0)

Еще одним дополнением интерфейсов в C# 8.0 является возможность наличия в них статических конструкторов и членов, которые функционируют аналогично статическим членам в определениях классов, но определены в интерфейсах. Добавьте к интерфейсу

IRegularPointy
статическое свойство и статический конструктор:

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
:

Поделиться с друзьями: