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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 110 111 112 113 114 115 116 117 118 ... 407
IShape : IDrawable, IPrintable

  {

    int GetNumberOfSides();

  }

}

На рис. 8.6 показана текущая иерархия интерфейсов.

Главный вопрос: сколько методов должен реализовывать класс, поддерживающий IShape? Ответ: в зависимости от обстоятельств. Если вы хотите предоставить простую реализацию метода Draw(), тогда вам необходимо реализовать только три члена, как иллюстрируется в следующем типе Rectangle:

using System;

namespace MiInterfaceHierarchy

{

  class Rectangle : IShape

  {

    public int GetNumberOfSides() => 4;

    public void Draw() => Console.WriteLine("Drawing...");

    public void Print() => Console.WriteLine("Printing...");

  }

}

Если вы предпочитаете располагать специфическими реализациями для каждого метода Draw() (что в данном случае имеет смысл), то конфликт имен можно устранить с использованием явной реализации интерфейсов, как делается в представленном далее типе Square:

namespace MiInterfaceHierarchy

{

  class Square : IShape

  {

    // Использование явной реализации для устранения

    // конфликта имен членов.

    void IPrintable.Draw()

    {

      // Вывести на принтер...

    }

    void IDrawable.Draw()

    {

      // Вывести на экран...

    }

    public void Print()

    {

      // Печатать...

    }

    public int GetNumberOfSides() => 4;

  }

}

В идеале к данному моменту вы должны лучше понимать процесс определения и реализации специальных интерфейсов с применением синтаксиса С#. По правде говоря, привыкание к программированию на основе интерфейсов может занять определенное время, так что если вы находитесь в некотором замешательстве, то это совершенно нормальная реакция.

Однако имейте в виду, что интерфейсы являются фундаментальным аспектом .NET Core. Независимо от типа разрабатываемого приложения (веб-приложение, настольное приложение с графическим пользовательским интерфейсом, библиотека доступа к данным и т.п.) работа с интерфейсами будет составной частью этого процесса. Подводя итог, запомните, что интерфейсы могут быть исключительно полезны в следующих ситуациях:

• существует единственная иерархия, в которой общее поведение поддерживается только подмножеством производных типов;

• необходимо моделировать общее поведение, которое встречается в нескольких иерархиях, не имеющих общего родительского класса кроме System.Object.

Итак, вы ознакомились со спецификой построения и реализации специальных интерфейсов. Остаток главы посвящен исследованию нескольких предопределенных интерфейсов, содержащихся в библиотеках базовых классов .NET Core. Как будет показано, вы можете реализовывать стандартные интерфейсы .NET Core в своих специальных типах, обеспечивая их бесшовную интеграцию с инфраструктурой. 

Интерфейсы IEnumerable и IEnumerator

Прежде чем приступать к исследованию процесса реализации существующих интерфейсов .NET Core, давайте сначала рассмотрим роль интерфейсов IEnumerable и IEnumerator. Вспомните, что язык C# поддерживает ключевое слово foreach, которое позволяет осуществлять проход по содержимому массива любого типа:

// Итерация по массиву элементов.

int[] myArrayOfInts = {10, 20, 30, 40};

foreach(int i in myArrayOfInts)

{

  Console.WriteLine(i);

}

Хотя может показаться, что данная конструкция подходит только для массивов, на самом деле foreach разрешено использовать с любым типом, который поддерживает метод GetEnumerator(). В целях иллюстрации создайте новый проект консольного приложения по имени CustomEnumerator. Скопируйте в новый проект файлы Car.cs и Radio.cs из проекта SimpleException, рассмотренного в главе 7. Не забудьте поменять пространства имен для классов на CustomEnumerator.

Теперь вставьте в проект новый класс Garage (гараж), который хранит набор объектов Car (автомобиль) внутри System.Array:

using System.Collections;

namespace CustomEnumerator

{

  // Garage содержит набор объектов Car.

  public class Garage

  {

    private Car[] carArray = new Car[4];

    // При запуске заполнить несколькими объектами Car.

    public Garage()

    {

      carArray[0] = new Car("Rusty", 30);

      carArray[1] = new Car("Clunker", 55);

      carArray[2] = new Car("Zippy", 30);

      carArray[3] = new Car("Fred", 30);

    }

  }

}

В идеальном случае было бы удобно проходить по внутренним элементам объекта Garage с применением конструкции foreach как в ситуации с массивом значений данных:

using System;

using CustomEnumerator;

// Код выглядит корректным...

Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****n");

Garage carLot = new Garage();

// Проход по всем объектам Car в коллекции?

foreach (Car c in carLot)

{

  Console.WriteLine("{0} is going {1} MPH",

    c.PetName, c.CurrentSpeed);

}

Console.ReadLine();

К сожалению, компилятор информирует о том, что в классе Garage не реализован метод по имени GetEnumerator(), который формально определен в интерфейсе IEnumerable, находящемся в пространстве имен System.Collections.

На заметку! В главе 10 вы узнаете о роли обобщений и о пространстве имен System.Collections.Generic. Как будет показано, это пространство имен содержит обобщенные версии интерфейсов IEnumerable/IEnumerator, которые предлагают более безопасный к типам способ итерации по элементам.

Классы или структуры, которые поддерживают такое поведение, позиционируются как способные предоставлять вызывающему коду доступ к элементам, содержащимся внутри них (в рассматриваемом примере самому ключевому слову foreach). Вот определение этого стандартного интерфейса:

// Данный интерфейс информирует вызывающий код о том,

// что элементы объекта могут перечисляться

public interface IEnumerable

{

   IEnumerator GetEnumerator();

}

Как видите, метод GetEnumerator() возвращает ссылку на еще один интерфейс по имени System.Collections.IEnumerator, обеспечивающий инфраструктуру, которая позволяет вызывающему коду обходить внутренние объекты, содержащиеся в совместимом с IEnumerable контейнере:

// Этот интерфейс позволяет вызывающему коду получать элементы контейнера.

public interface IEnumerator

{

   bool MoveNext ();   // Переместить вперед внутреннюю позицию курсора.

   object Current { get;}  // Получить текущий элемент

                           // (свойство только для чтения).

   void Reset (); // Сбросить курсор в позицию перед первым элементом.

}

Если вы хотите обновить тип Garage для поддержки этих интерфейсов, то можете пойти длинным путем и реализовать каждый метод вручную. Хотя вы определенно вольны предоставить специализированные версии методов GetEnumerator(), MoveNext(), Current и

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

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