static void Main(string[] args) {
// вызов члена Points интерфейса IPointy.
Hexagon hex = new Hexagon();
Console.WriteLine("Вершин: {0}", hex.Points);
Console.ReadLine();
}
Этот подход прекрасно работает в данном конкретном случае, поскольку вы знаете, что тип Hexagon реализует упомянутый интерфейс. Однако в других случаях во время компиляции вы не сможете определить, какие интерфейсы поддерживаются данным типом. Предположим, например, что у нас есть массив из 50 типов, соответствующих Shape, но только некоторые из них поддерживают IPointy. Очевидно, что если вы попытаетесь вызвать свойство Points для типа, в котором IPointy не реализован, вы получите ошибку компиляции. Возникает следующий вопрос: "Как динамически получить информацию о множестве интерфейсов, поддерживаемых данным типом?"
Выяснить во время выполнения, поддерживает ли данный тип конкретный интерфейс можно, например, с помощью явного вызова. Если тип не поддерживает запрошенный интерфейс, вы получите исключение InvalidCastException. Чтобы "изящно" обработать эту возможность, используйте структурированную обработку исключений, например:
static void Main(string[] args) {
…
// Возможный захват исключения InvalidCastException.
Circle с = new Circle ("Lisa");
IPointу itfPt;
try {
itfPt = (IPointy)c;
Console.WriteLine(itfPt.Points);
} catch (InvalidCastException e) {
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
Итак, можно использовать логику try/catch и надеяться на удачу, но лучше еще до вызова членов интерфейса определить, какие интерфейсы поддерживаются. Мы рассмотрим два варианта такой тактики.
Получение интерфейсных ссылок: ключевое слово as
Второй способ проверить поддержку интерфейса для данного типа предполагает использование ключевого слова as, о котором уже шла речь в главе 4. Если объект можно интерпретировать, как указанный интерфейс, будет возвращена ссылка на интерфейс. Если нет – вы получите null.
static void Main(string[] args) {
…
// Можно ли интерпретировать hex2, как IPointy?
Hexagon hex2 = new Hexagon("Peter");
IPointy itfPt2 = hex2 as IPointy;
if (itfPt2 != null) Console.WriteLine("Вершин: {0}" , itfPt2.Points);
else Console.WriteLine("ОЙ! Вершин не видно…");
}
Обратите внимание на то, что при использовании ключевого слова as не возникает необходимости использовать логику try/catch, поскольку в том случае, когда ссылка оказывается непустой, вы гарантированно будете иметь действительную ссылку на интерфейс.
Получение интерфейсных ссылок: ключевое слово is
Можно также проверить реализацию интерфейса с помощью ключевого слова is. Если соответствующий объект не совместим указанным интерфейсом, будет возвращено значение false. А если тип совместим с интерфейсом, вы можете смело вызвать его члены без использования логики try/catch.
Для примера предположим, что мы изменили массив типов Shape так, что теперь некоторые его члены реализуют IPointy. Вот как с помощью ключевого слова is можно выяснить, какие из элементов в массиве поддерживают этот интерфейс.
static void Main(string[] args) {
…
Shape[] s = {new Hexagon(), new Circle(), new Triangle("Joe"), new Circle("JoJo")};
for (int i = 0; i ‹ s.Length; i++) {
// Напомним, что базовый класс Shape определяет абстрактный
// член Draw(), поэтому все формы могут отображать себя.
s[i].Draw()
// Кто с вершинами?
if (s[i] is IPointy) Console.WriteLine("-› Вершин: {0} ", ((IPointy)s[i]).Points);
else Console.WriteLine("-› {0} без вершин!", s[i].PetName);
}
}
Соответствующий вывод показан на рис. 7.2.
Рис 7.2. Динамическое обнаружение реализованных интерфейсов
Интерфейсы в качестве параметров
Поскольку интерфейсы являются полноценными типами .NET, вы можете конструировать методы, которые будут использовать интерфейсы, как параметры. Для примера предположим, что мы определили другой интерфейс с именем IDraw3D.
// Моделируем возможность отображения типа в пространстве.
public interface IDraw3D {
void Draw3D();
}
Предположим также, что две из наших трех форм (Circle и Hexagon) сконфигурированы для поддержки этого нового поведения.
// Circle поддерживает IDraw3D.
public class Circle: Shape, IDraw3D{
…
public void Draw3D() {
Читать дальше