void Draw();
}
}
// IPrintable.cs
namespace MiInterfaceHierarchy
{
interface IPrintable
{
void Print();
void Draw(); // < -- Здесь возможен конфликт имен!
}
}
// IShape.cs
namespace MiInterfaceHierarchy
{
// Множественное наследование интерфейсов. Нормально!
interface 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
: