Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 107 108 109 110 111 112 113 114 115 ... 407
class="p1">  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)

    {

      return ip;

    }

  }

  return null;

}

Взаимодействовать с методом FindFirstPointyShape() можно так:

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

// Создать массив элементов Shape.

Shape[] myShapes = { new Hexagon(), new Circle(),

                     new Triangle("Joe"), new Circle("JoJo")};

// Получитгь первый элемент, имеющий вершины.

IPointy firstPointyItem = FindFirstPointyShape(myShapes);

// В целях безопасности использовать 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();

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

Автоматическая реализация интерфейсов

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

1 ... 107 108 109 110 111 112 113 114 115 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий