Поздняя привязка - Late binding


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

При раннем связывании или статическом связывании в объектно-ориентированном языке на этапе компиляции исправляются все типы переменных и выражений. Обычно это сохраняется в скомпилированной программе как смещение в таблице виртуальных методов («v-table»). Напротив, при позднем связывании компилятор не считывает достаточно информации, чтобы проверить, существует ли метод, или привязать свой слот к v-таблице. Вместо этого метод ищется по имени во время выполнения.

Основное преимущество использования позднего связывания в программировании модели компонентных объектов (COM) состоит в том, что компилятору не требуется ссылаться на библиотеки, содержащие объект, во время компиляции . Это делает процесс компиляции более устойчивым к конфликтам версий, в которых v-таблица класса может быть случайно изменена. (Это не является проблемой для платформ, скомпилированных с своевременной компиляцией, таких как .NET или Java, потому что v-таблица создается во время выполнения виртуальной машиной для библиотек по мере их загрузки в работающее приложение.)

История

Термин «позднее связывание» восходит как минимум к 1960-м годам, где его можно найти в Сообщениях ACM . Этот термин широко использовался для описания соглашений о вызовах в таких языках, как Lisp, хотя обычно с негативным подтекстом о производительности.

В 1980-х годах Smalltalk популяризировал объектно-ориентированное программирование (ООП) и, как следствие, позднее связывание. Алан Кей однажды сказал: «ООП для меня означает только обмен сообщениями, локальное сохранение, защиту и сокрытие состояния-процесса, а также крайнее позднее связывание всех вещей. Это можно сделать в Smalltalk и в LISP. что это возможно, но я о них не знаю ".

В начале и середине 1990-х годов Microsoft активно продвигала свой стандарт COM как двоичный интерфейс между различными языками программирования ООП. COM-программирование в равной степени способствовало раннему и позднему связыванию, при этом многие языки поддерживают и то, и другое на уровне синтаксиса.

В 2000 году Алекс Мартелли ввел термин « утиная печать » для обозначения аналогичной концепции, но с другим акцентом. В то время как позднее связывание обычно фокусируется на деталях реализации, утиная типизация фокусируется на способности игнорировать типы и концентрироваться на методах, которые в настоящее время есть у объекта.

Поздние реализации привязки

Позднее связывание в объектно-ориентированных языках с динамической типизацией

В большинстве языков с динамической типизацией список методов объекта может быть изменен во время выполнения. Это требует позднего связывания.

Позднее связывание в Лиспе

В Лиспе . Вызов глобальных функций с поздним связыванием эффективно просматривается во время выполнения через функциональную ячейку символа . Эти привязки функций изменяемы.

Пример использования интерактивного сеанса Clozure Common Lisp :

? (defun foo ()
    (bar pi))   ; a still undefined function BAR gets called
;Compiler warnings :
;   In FOO: Undefined function BAR
FOO

? (defun bar (x)   ; now we define it
    (* x 2))
BAR

? (foo)    ; calling foo and it uses the recent definition of BAR
6.283185307179586D0

? (defun bar (x)   ; now we redefine BAR
    (* x 1000))
BAR

? (foo)    ;  FOO now calls the new function, there is no need to recompile/link/load FOO
3141.592653589793D0

? (type-of 'bar)   ;  BAR is a symbol
SYMBOL

? (symbol-function 'bar)  ; the symbol BAR has a function binding
#<Compiled-function BAR #x302000D1B21F>

Позднее связывание в C ++

В C ++ поздняя привязка (также называемая «динамической привязкой») относится к тому, что обычно происходит, когда virtualключевое слово используется в объявлении метода. Затем C ++ создает так называемую виртуальную таблицу, которая представляет собой таблицу поиска для таких функций, к которым всегда будут обращаться при их вызове. Обычно термин «поздняя привязка» используется в пользу « динамической отправки ».

Позднее связывание в языках COM

В программировании COM вызов метода с поздним связыванием выполняется с помощью интерфейса IDispatch . Некоторые языки на основе COM, такие как Visual Basic 6, имеют синтаксическую поддержку для вызова этого интерфейса. Это делается путем определения типа переменной как Object. Другие, такие как C ++, требуют, чтобы вы явно вызывали GetIDsOfNames для поиска метода и Invoke для его вызова.

Позднее связывание в .NET

В .NET под поздним связыванием понимается переопределение такого virtualметода, как C ++, или реализация интерфейса. Компилятор создает виртуальные таблицы для каждого вызова виртуального метода или метода интерфейса, который используется во время выполнения для определения реализации, которую нужно выполнить.

Так же, как COM и Java, Common Language Runtime предоставляет API-интерфейсы отражения, которые могут выполнять вызовы позднего связывания. Использование этих вызовов зависит от языка.

В C # 4 язык также добавил «динамический» псевдотип. Это будет использоваться вместо типа объекта, чтобы указать, что желательна поздняя привязка. Конкретный необходимый механизм позднего связывания определяется во время выполнения с использованием среды выполнения динамического языка в качестве отправной точки.

Visual Basic использует их всякий раз, когда переменная имеет тип Object и действует директива компилятора «Option Strict Off». Это настройка по умолчанию для нового проекта VB. До версии 9 только объекты .NET и COM могли связываться с опозданием. В VB 10 это было расширено до объектов на основе DLR.

Поздняя привязка в Java

В Java есть три определения позднего связывания.

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

В настоящее время в программировании на Java популярно использовать термин «позднее связывание» как синоним динамической отправки . В частности, это относится к единому механизму диспетчеризации Java, используемому с виртуальными методами.

Наконец, Java может использовать позднее связывание, используя свои API отражения и интроспекцию типов, почти так же, как это делается в программировании на COM и .NET. Вообще говоря, те, кто программирует только на Java, не называют эту позднюю привязку. Точно так же использование методов «утиной печати» не одобряется в программировании на Java, вместо этого используются абстрактные интерфейсы.

Oracle, нынешний владелец Java, как известно, использовал термин позднее связывание в смысле «утиной печати» при обсуждении как Java, так и других языков в одной и той же документации.

Раннее и позднее связывание в PL / SQL и Ada

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

При использовании позднего связывания проверка отметки времени не выполняется, а хранимая процедура выполняется через анонимный блок PL / SQL. Хотя это может быть медленнее, это устраняет необходимость перекомпилировать все клиентские приложения при изменении хранимой процедуры.

Это различие является уникальным для PL / SQL и Ada. Другие языки, которые могут вызывать процедуры PL / SQL, а также другие механизмы баз данных, используют только позднее связывание.

Критика

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

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

Позднее связывание может помешать статическому анализу, необходимому для интегрированной среды разработки (IDE). Например, функция IDE «перейти к определению» может не работать при вызове с поздним связыванием, если у IDE нет способа узнать, к какому классу может относиться вызов. Современная среда IDE легко решает эту проблему, особенно для объектно-ориентированных языков, поскольку метод с поздним связыванием всегда указывает интерфейс или базовый класс, к которому ведет «перейти к определению», а «найти все ссылки» можно использовать для поиска всех реализаций или отменяет.

Похожая проблема заключается в том, что возможное отсутствие типизированной информации может помешать созданию графов зависимостей. Однако другие методы программирования, такие как абстрактные интерфейсы, могут привести к тем же проблемам. Современная IDE может создавать такие графы зависимостей так же легко, как и «найти все ссылки».

Смотрите также

использованная литература