Интерфейсы
Вероятно, вы убедились в том, что наследование занимает важное место в VB.NET, но для полноценного использования объектно-ориентированных средств VB.NET вам также придется освоить реализацию интерфейсов. Этой важной теме посвящены несколько ближайших разделов.
Прежде всего реализацию интерфейса можно рассматривать как контракт, обязательный для любого класса. Интерфейсы занимали важнейшее место в программировании СОМ, а также в реализации объектно-ориентированных средств в прежних версиях VB. При реализации интерфейса класс-обязуется предоставлять некоторую функциональность в соответствии с сигнатурами заголовков членов, отныне и во веки веков. В отличие от объектов при наследовании интерфейсы не связаны никакими взаимными зависимостями – каждая реализация интерфейса существует независимо от других.
Примечание
В мире ООП часто приходится слышать высказывания типа "композиция предпочтительнее наследования" (то есть лучше использовать интерфейсы, а не наследование). Благодаря средствам контроля версии в .NET выбор между наследованием и композицией уже не играет столь принципиальной роли. Используйте наследование всюду, где это уместно, – там, где существует ярко выраженная связь типа "является частным случаем".
Реализация интерфейса предполагает, что ваш класс содержит методы со строго определенными сигнатурами. Эти методы могут быть пустыми, но они обязательно должны присутствовать.
Фактическая реализация методов не фиксируется; как было только что сказано, методы могут вообще ничего не делать. Поддержка интерфейса – всего лишь обязательство определить методы с заданными сигнатурами. Из этого простого факта вытекает множество замечательных следствий. Особый интерес представляют следующие:
- "умный" компилятор может заменить вызовы функций быстрым поиском по таблице с последующей передачей управления;
- с точки зрения программиста – разработчики могут вызывать методы вашего класса по сигнатуре, не опасаясь, что указанный метод не существует;
- при наличии подобных обязательств компилятор может использовать полиморфизм так же, как это делается при наследовании.
Примечание
При вызове метода, реализованного в составе интерфейса, компилятор .NET еще на стадии компиляции может вычислить вызываемый метод на основании сигнатуры и типа класса (это называется ранним связыванием). Этот факт объясняет возможность использования полиморфизма при реализации интерфейсов.
А теперь подумайте, что произойдет, если:
- вы не будете связаны обязательством на поддержку метода с заданной сигнатурой в результате реализации интерфейса;
- ваш класс не входит в иерархию наследования, в которой VB.NET сможет найти метод с нужной сигнатурой.
Происходит следующее: в режиме жесткой проверки типов (Option StrictOn) программа вообще не будет компилироваться. Если этот режим отключить, умный компилятор .NET поймет, что вызов метода класса не удастся заменить в откомпилированном коде неким подобием простого вызова функции. Таким образом, компилятору придется сгенерировать значительно больший объем кода. Фактически он должен во время выполнения программы вежливо спросить у объекта, поддерживает ли он метод с указанной сигнатурой, и если поддерживает – не будет ли он возражать против его вызова? Подобное решение обладает двумя характерными особенностями, из-за которых оно работает значительно медленнее и гораздо чаще приводит к возникновению ошибок:
- Необходимо предусмотреть обработку ошибок на случай непредвиденных ситуаций.
- Поскольку компилятор на стадии компиляции не может определить, по какому адресу следует передать управление в блоке памяти, занимаемом объектом, ему приходится полагаться на косвенные методы передачи управления на стадии выполнения.
Описанный процесс называется поздним связыванием (late binding). Он не только значительно уступает раннему связыванию по скорости, но и вообще не разрешен при включенном режиме Option Strict за исключением позднего связывания, основанного на применении рефлексии.