Категории
Самые читаемые
onlinekniga.com » Компьютеры и Интернет » Программирование » ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

Читать онлайн ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 61 62 63 64 65 66 67 68 69 ... 259
Перейти на страницу:

   if (s[i] is IDraw3D) DrawIn3D((IDraw3D)s[i]);

  }

 }

}

Обратите внимание на то, "что треугольник не отображается, поскольку он не является IDraw3D-совместимым (рис. 7.4).

Рис.7.4. Интерфейсы в качестве параметров

Интерфейсы в качестве возвращаемых значений

Интерфейсы можно использовать и в качестве возвращаемых значений методов. Например, можно создать метод, который берет любой System.Object, проверяет на совместимость с IPointy и возвращает ссылку на извлеченный интерфейс.

// Этот метод проверяет соответствие IPointy и, если это возможно,

// возвращает ссылку на интерфейс.

static IPointy ExtractPointyness(object o) {

 if (o is IPointy) return (IPointy)o;

 else return null;

}

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

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");

};

Массивы интерфейсных типов

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

Эти иерархии абсолютно не связаны между собой с точки зрения классического наследования, но с ними можно обращаться полиморфно, используя программирование на основе интерфейсов. Для иллюстрации предположим, что у нас есть массив объектов, совместимых с 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.

Явная реализация интерфейса

В определении 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().

Теперь рассмотрим родственную проблему. Что делать, если вам нужно гарантировать, что методы, определенные данным интерфейсом, будут доступны только с помощью интерфейсных ссылок, а не объектных? В настоящий момент члены, определенные интерфейсом IPointy, доступны как с помощью объектных ссылок, так и по ссылке на IPointy.

Ответ на оба вопроса дает явная реализация интерфейса. Используя этот подход, вы можете гарантировать, что пользователь объекта сможет получить доступ К методам, определенным данным интерфейсом, только с помощью правильной интерфейсной ссылки, не допуская при этом конфликта имен. Для примера рассмотрите следующий обновленный класс Line (предполагая, что вы соответствующим образом обновили Hexagon и Circle).

// Используя явную реализацию метода, можно указать

// другие реализации Draw() .

public class Line: Shape, IDraw3D {

 // Этот метод можно вызвать только ссылкой на интерфейс IDraw3D.

 void IDraw3D.Draw() { Console.WriteLine("Отображение ЗD-линии…"); }

 // Это можно вызвать только на уровне объекта.

 public override void Draw() { Console.WriteLine("Отображение линии…"); }

 …

}

Как видите, при явной реализации члена интерфейса общий шаблон выглядит так: возвращаемоеЗначение ИмяИнтерфейса.ИмяМетода(аргументы). Здесь есть несколько "подводных камней", о которых следует знать. Прежде всего, не допускается определять явно реализуемые члены с модификаторами доступа. Например, следующий синтаксис недопустим.

// Нет! Это недопустимо.

public class Line: Shape, IDraw3D {

 public void IDraw3D.Draw() { // ‹= Ошибка!

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

 }

 …

}

Главной причиной использования явной реализации метода интерфейса является необходимость "привязки" соответствующего метода интерфейса к уровню интерфейса. Если добавить ключевое слово public, то это будет означать, что данный метод является членом открытого сектора класса, и "привязка" будет отменена. Тогда вызывающая сторона сможет вызывать только метод Draw(), определенный базовым классом Shape на объектном уровне.

// Здесь вызывается переопределенный метод Shape.Draw().

Line myLine = new Line();

myLine.Draw();

Чтобы вызвать метод Draw(), определенный с помощью IDraw3D, мы должны явно получить интерфейсную ссылку, используя любой из ранее указанных подходов. Например:

// Это обеспечит вызов метода IDraw3D.Draw().

Line myLine = new Line();

IDraw3D i3d = (IDraw3D) myLine;

i3d.Draw();

Разрешение конфликтов имен

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

// Три интерфейса, определяющие методы с одинаковыми именами.

public interface IDraw {

 void Draw();

}

public interface IDrawToPrinter {

 void Draw();

}

Если вы захотите построить класс с именем SuperImage (суперизображение), поддерживающий базовую визуализацию (IDraw), 3D-визуализацию (IDraw3D), а также сервис печати (IDrawToPrinter), то единственным способом обеспечить уникальную реализацию для каждого метода будет использование явной реализации интерфейса.

// Не выводится из Shape, но вводит конфликт имен.

public class SuperImage: IDraw, IDrawToPrinter, IDraw3D {

 void IDraw.Draw() {/* Логика базовой визуализации. */}

 void IDrawToPrinter.Draw() {/* Логика печати. */}

 void IDraw3D.Draw() {/* Логика 3D-визуализации. */}

}

Исходный код. Проект CustomInterface размешен в подкаталоге, соответствующем главе 7.

1 ... 61 62 63 64 65 66 67 68 69 ... 259
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен.
Комментарии