С этим методом можно взаимодействовать так, как предлагается ниже.

static void Main(string[] args) {

 // Попытка извлечь IPointy из объекта Car.

 Car myCar = new Car;

 IPointy itfPt = ExtractPointyness(myCar);

 if (itfPt!= null) Console.WriteLine("Объект имеет {0} вершин.", itfPt.Points);

 else Console.WriteLine("Этот объект не реализует IPointy");

};

<p>Массивы интерфейсных типов</p>

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

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

static void Main(string[] args) {

 // Этот массив может содержать только типы,

 // реализующие интерфейс IPointy.

 IPointy[] myPointyObjects = {new Hexagon, new Knife, new Triangle, new Fork, new PitchFork};

 for (int i = 0; i ‹ myPointyObjects.Length; i++) Console.WriteLine("Объект имеет {0} вершин", myPointyObjects[i].Points);

}

Замечание. С учетом общеязыковой природы .NET важно подчеркнуть, что можно определить интерфейс на одном языке (C#), а реализовать его на другом (VB .NET). Но чтобы выяснить, как это сделать, нам потребуется понимание структуры компоновочных блоков .NET, что является темой обсуждения главы 11.

<p>Явная реализация интерфейса</p>

В определении IDraw3D мы были вынуждены назвать наш единственный метод Draw3D, чтобы избежать конфликта с абстрактным методом Draw, определенным в базовом классе Shape. Такое определение интерфейса вполне допустимо, но более естественным именем для метода было бы Draw.

// Изменение имени с "Draw3D" на "Draw".

public interface IDraw3D {

 void Draw;

}

Если вносить такое изменение, то потребуется также обновить нашу реализацию DrawIn3D.

public static void DrawIn3D(IDraw3D itf3d) {

 Console.WriteLine("-› Отображение IDraw3D-совместимоuо типа");

 itf3d.Draw;

}

Теперь предположим, что мы определили новый класс Line (линия), который получается из абстрактного класса Shape и реализует iDraw3D (оба из них теперь определяют одинаково названные абстрактные методы Draw).

// Проблемы? Это зависит.

public class Line: Shape, IDraw3D {

 public override void Draw {

  Console.WriteLine("Отображение линии…");

 }

}

Класс Line компилируется беспрепятственно. Рассмотрим следующую логику Main.

static void Main(string[] args) {

 …

 // Вызов Draw.

 Line myLine = new Line;

 myLine.Draw;

 // Вызов той же реализации Draw!

 IDraw3D itfDraw3d = (IDraw3D)myLine;

 itfDraw3d.Draw;

}

С учетом того, что вы уже знаете о базовом классе Shape и интерфейсе IDraw3D, это выглядит так как будто вы вызываете два варианта метода Draw (один с объектного уровня, а другой – с помощью интерфейсной ссылки). Однако компилятор способен вызывать одну и ту же реализацию и с помощью интерфейса, и с помощью объектной ссылки, поскольку абстрактный базовый класс Shape и интерфейс IDraw3D имеют одинаково названные члены. Это может оказаться проблемой, когда вы хотите, чтобы метод IDraw3D.Draw представлял тип во всей трехмерной (3D) "красе", а не в неказистом двухмерном представлении переопределённого метода Shape.Draw.

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

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