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 в интерфейсе не определены, а потому на этапе компиляции возникнут ошибки.

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

<p id="AutBody_Root329">Статические конструкторы и члены (нововведение в версии 8.0)</p>

Еще одним дополнением интерфейсов в 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}");

Обратите внимание, что к статическому свойству необходимо обращаться через интерфейс, а не переменную экземпляра.

<p id="AutBody_Root330">Использование интерфейсов в качестве параметров</p>

Учитывая, что интерфейсы являются допустимыми типами, можно строить методы, которые принимают интерфейсы в качестве параметров, как было проиллюстрировано на примере метода CloneMe() ранее в главе. Предположим, что вы определили в текущем примере еще один интерфейс по имени IDraw3D:

namespace CustomInterfaces

{

  // Моделирует способность визуализации типа в трехмерном виде.

  public interface IDraw3D

  {

    void Draw3D();

  }

}

Далее сконфигурируйте две из трех фигур (Circle и Hexagon) с целью поддержки нового поведения:

// Circle поддерживает IDraw3D.

Перейти на страницу:

Похожие книги