Динамическое использование интерфейсов
Полезной особенностью интерфейсов является возможность их использования в динамических сценариях, что позволяет по ходу выполнения программы проверять, поддерживается ли интерфейс определенным классом. Если интерфейс поддерживается, мы можем воспользоваться предоставляемыми им возможностями; в противном же случае программа может проигнорировать интерфейс. Фактически такое динамическое поведение реализуется с помощью обработки возникающих исключений, как это уже было продемонстрировано выше. Хотя подобный подход вполне работоспособен, однако он отличается некоторой неуклюжестью и приводит к появлению трудночитаемых программ. C++ поддерживает использование операторов dynamic_cast и typeid, а в .NET Framework есть класс Type (Тип), облегчающий динамическое использование интерфейсов.
В качестве примера рассмотрим интерфейс ICustomer2, имеющий, по сравнению с интерфейсом ICustomer 1, дополнительный метод ShowCustomer.
_gc _interface ICustomer2: ICustomerl // сборщик мусора – ICustomer2: ICustomerl { public: void ShowCustomers(int id); // идентификатор };
Предположим, что класс Customerl поддерживает интерфейс ICustomer1, a класс Customer2 – интерфейс ICustomer2. Для консольной программы-клиента удобнее использовать исходный метод ShowCustomer, а не метод GetCustomer, так как последний создает список массивов и копирует данные в него. Поэтому программа-клиент предпочтет работать с интерфейсом ICustomer2, если это возможно. В папке TestlnterfaceBeforeCast содержится программа, рассмотренная в следующем разделе.
Проверка поддержки интерфейса перед приведением типов
Проверку поддержки интерфейса можно производить, выполняя динамическое приведение типа указателя и обрабатывая исключение, которое может при этом возникнуть. Однако более изящным решением будет выполнять проверку до приведения типа, избегая при этом возникновения исключений. Если объект поддерживает необходимый интерфейс, можно выполнять приведение типа для получения доступа к интерфейсу. С# поддерживает использование удобного оператора is для проверки того, поддерживает ли объект определенный интерфейс. К сожалению, в C++ с этой целью приходится использовать метод отражения, реализуемый посредством методов GetType и Getlnterfасе. В связи с тем, что это приводит к появлению несколько громоздкого выражения, в следующем примере с помощью директивы #define определяется макрос IS (THIS, THAT_INTERFACE), используемый далее в двух условных операторах if.
//TestlnterfaceBeforeCast.срр //MACRO: pObj › GetType() › GetInterface("Somelnterface")!=0 // МАКРОС #define IS(THIS, THAT_INTERFACE) (THIS › GetType() › GetInterface( THAT_INTERFACE)!=0) #using <mscorlib.dll> using namespace System; // использование пространства имен Система; _gc _interface ICustomer1 {}; // сборщик мусора – ICustomer1; _gc _interface ICustomer2: ICustomer1 // сборщик мусора – ICustomer2: ICustomer1 { public: void ShowCustomers(int id); // идентификатор }; _gc class Customer!: public ICustomer1 {}; // класс сборщика мусора Customerl: ICustomerl {}; _gc class Customer2: public ICustomer2 // класс сборщика мусора Customer2: ICustomer2 { public: void ShowCustomers(int id) // идентификатор { Console::WriteLine("Customer2::ShowCustomers: succeeded"); } }; void main(void) // главный { Customerl *pCustl = new Customerl; // не к ICustomer2 Console::WriteLine(pCustl › GetType()); // проверить, относится ли к типу ICustomer2 перед приведением if (IS(pCustl, "ICustomer2")) { ICustomer2 *plcust2 = dynamic_cast<ICustomer2 *>(pCustl); plcust2 › ShowCustomers(-1); } else Console::WriteLine ("pCustl does not support ICustomer2 interface"); // ("pCustl не поддерживает интерфейс ICustomer2"); Customer2 *pCust2 = new Customer2; // да, на ICustomer2 Console::WriteLine(pCust2 › GetType()); // проверить, относится ли к типу ICustomer2 перед приведением if (IS(pCust2, "ICustomer2")) { ICustomer2 *plcust2 = dynamic_cast<ICustomer2 *>(pCust2); pIcust2 › ShowCustomers(-1); } else Console::WriteLine ("pCust2 does not support ICustomer2 interface"); // ("pCust2 не поддерживает интерфейс ICustomer2"); }