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

83

Я пытаюсь понять, когда в C # происходит раннее / позднее связывание.

Невиртуальные методы всегда связываются рано. Виртуальные методы всегда имеют позднюю привязку: компилятор вставляет дополнительный код для разрешения фактического метода для привязки во время выполнения и проверяет безопасность типов. Таким образом, полиморфизм подтипа использует позднее связывание.

Вызов методов с использованием отражения - пример позднего связывания. Для этого мы пишем код, а не компилятор. (Например, вызов COM-компонентов.)

VB.NET поддерживает неявное позднее связывание, когда Option Strict выключено. Объект связывается поздно, когда он назначается переменной, объявленной как объект типа. Компилятор VB вставляет код для привязки к нужному методу во время выполнения и для перехвата недопустимых вызовов. C # не поддерживает эту функцию.

Я иду в правильном направлении?

А как насчет вызова делегатов и вызова метода через ссылку на интерфейс? Это раннее или позднее связывание?

Cybermaxs
источник

Ответы:

103

В C # все привязано заранее, если вы не используете интерфейс Reflection.

Ранняя привязка просто означает, что целевой метод найден во время компиляции и создается код, который его вызовет. Независимо от того, виртуальный он или нет (это означает, что есть дополнительный шаг, чтобы найти его во время вызова, не имеет значения). Если метод не существует, компилятор не сможет скомпилировать код.

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

Большинство языков сценариев используют позднее связывание, а скомпилированные языки используют раннее связывание.

C # (до версии 4) не связывает поздно; Однако они могут использовать для этого API отражения. Этот API компилируется в код, который ищет имена функций, копаясь в сборках во время выполнения. VB может выполнить позднее связывание, если параметр Option Strict отключен.

Связывание обычно влияет на производительность. Поскольку для позднего связывания требуется поиск во время выполнения, это обычно означает, что вызовы методов выполняются медленнее, чем вызовы методов раннего связывания.


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

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

Итак, для виртуальной функции выполняется поиск. Это сильно оптимизировано, поэтому во время выполнения это произойдет очень быстро.

Ранняя граница

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

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

  • Поиск займет больше времени, потому что это не простой расчет смещения, обычно требуется сравнение текста.
  • Целевая функция может не существовать.
  • Целевая функция может не принимать переданные ей аргументы и может иметь возвращаемое значение неправильного типа.
  • В некоторых реализациях целевой метод может фактически изменяться во время выполнения. Таким образом, поиск может выполнять другую функцию. Я думаю, что это происходит на языке Ruby, вы можете определить новый метод для объекта во время работы программы. Позднее связывание позволяет вызовам функций начинать вызывать новое переопределение для метода вместо вызова существующего базового метода.
Скотт Лэнгэм
источник
Я думаю, вы хотели сказать: «Язык VB сам по себе не связывает поздно ...»
Майкл Медоуз,
На самом деле, я не использую VB ... поэтому я мало что знаю об этом. Я имел в виду C #, но, похоже, я просто повторял себя. Я полагаю, что вы правы, поэтому я исправлю это!
Скотт Лэнгхэм,
21
Динамическая типизация - это не то же самое, что позднее связывание. Разница небольшая, но важная. Позднее связывание по-прежнему связывается с типом, оно выполняется только во время выполнения. Динамическая типизация не связывает; вместо этого он разрешает информацию о членах во время выполнения независимо от типа.
Майкл Медоуз,
1
« Для объекта, у которого есть какие-либо виртуальные методы, компилятор сгенерирует v-таблицу. ». Это немного неверно - «класс», а не «объект».
turdus-merula
1
@IvanRuski Я так не думаю. Во время компиляции известны все типы аргументов, которые принимает делегат. Таким образом, во время компиляции (которое «рано»), а не во время выполнения (которое «поздно»), компилятор может гарантировать, что вызов будет работать.
Скотт Лэнгхэм
18

C # 3 использует раннее связывание.

C # 4 добавляет позднее связывание с dynamicключевым словом. См . Запись в блоге Криса Берроу по этому поводу.

Что касается виртуальных и невиртуальных методов, это другой вопрос. Если я позвоню string.ToString(), код C # будет привязан к виртуальному object.ToString()методу. Код вызывающей стороны не меняется в зависимости от типа объекта. Скорее, виртуальные методы вызываются через таблицу указателей функций. Экземпляр объекта относится к таблице объекта, указывающей на его ToString()метод. У экземпляра строки есть таблица виртуальных методов, указывающая на этот ToString()метод. Да, это полиморфизм. но это еще не поздно.

Джо Эриксон
источник
1
Я не полностью согласен с этим объяснением. В C # маркировка метода или поля экземпляра как виртуального означает, что производный тип может переопределить реализацию базового типа в цепочке наследования. С виртуальными методами среда CLR знает, какой метод вызывать во время выполнения, на основе экземпляра объекта времени выполнения. Пожалуй, единственное, в чем я с вами согласен, это реализация полиморфизма. Затем вы вводите в заблуждение, говоря, что это еще не поздно. Это позднее связывание, поскольку среда CLR может вызвать правильную реализацию типа времени выполнения только в том случае, если ей известен тип среды выполнения экземпляра объекта.
Юлиус Депулла 07
6

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

//Early Binding
**Employee** employeeObject = new **Employee**();
employeeObject.CalculateSalary();

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

Прасад
источник
3

Это очень старый пост, но я хотел добавить к нему больше информации. Позднее связывание используется, когда вы не хотите создавать экземпляр объекта во время компиляции. В C#использовании Activatorдля вызова привязки объекта во время выполнения.

Кумар Нитеш
источник
3

Раннее связывание

Само название говорит о том, что компилятор знает, что это за объект, какие методы и свойства он содержит. Как только вы объявите объект, .NET Intellisense заполнит его методы и свойства при нажатии кнопки с точкой.

Общие примеры:

ComboBox cboItems;

ListBox lstItems; В приведенных выше примерах, если мы введем cboItem и поставим точку, за которой следует, он автоматически заполнит все методы, события и свойства поля со списком, потому что компилятор уже знает, что это поле со списком.

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

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

Общие примеры:

Object objItems;

objItems = CreateObject («Имя DLL или сборки»); Здесь во время компиляции тип objItems не определяется. Мы создаем объект dll и назначаем его objItems, поэтому все определяется во время выполнения.

Раннее связывание против позднего связывания

Теперь перейдем к картине ...

Приложение будет работать быстрее при раннем связывании, поскольку здесь не выполняется упаковка или распаковка.

Проще написать код в ранней привязке, так как intellisense будет заполнен автоматически

Минимальные ошибки при раннем связывании, поскольку синтаксис проверяется во время самой компиляции.

Позднее связывание будет поддерживаться во всех версиях, поскольку все решается во время выполнения.

Минимальное влияние кода на будущие улучшения, если используется Late Binding.

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

РаджГанеш
источник
2

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

http://www.youtube.com/watch?v=s0eIgl5iqqQ&list=PLAC325451207E3105&index=55&feature=plpp_video

http://www.youtube.com/playlist?list=PLAC325451207E3105

Суреш
источник
1

Эта статья представляет собой руководство по созданию компонента .net, использованию его в проекте Vb6 во время выполнения с использованием позднего связывания, присоединения его событий и получения обратного вызова.

http://www.codeproject.com/KB/cs/csapivb6callback2.aspx

Эта статья представляет собой руководство по созданию компонента .NET и его использованию в проекте VB6. Есть много примеров по этой проблеме, так зачем я написал новый? По моему скромному мнению, в других статьях недостающей частью является прикрепление своего события во время выполнения. Итак, в этой статье мы создадим компонент .NET, отметим его как видимый компонент COM, будем использовать его во время выполнения в VB6 и присоединим к его событиям.

https://www.codeproject.com/Articles/37127/Internet-Explorer-Late-Binding-Automation

Большинству разработчиков часто требуется автоматизация Internet Explorer, что в основном означает открытие браузера, заполнение некоторых форм и программную публикацию данных.

Наиболее распространенный подход - использовать shdocvw.dll (элемент управления Microsoft Web Browser) и Mshtml.dll (компонент анализа и рендеринга HTML) или Microsoft.Mshtml.dll, который на самом деле является оболочкой .NET для Mshtml.dll. Вы можете получить дополнительную информацию об Internet Explorer - о браузере здесь.

Если вы выберете вышеуказанный метод и библиотеки DLL, давайте посмотрим на некоторые проблемы, с которыми вам, возможно, придется столкнуться:

Вы должны распространять эти библиотеки DLL, потому что ваш проект будет зависеть от этих библиотек DLL, и это серьезная проблема, если вы не можете правильно их развернуть. Просто погуглите о проблемах с распространением shdocvw и mshtml.dll, и вы поймете, о чем я говорю. Вам необходимо развернуть Microsoft.mshtml.dll размером 8 МБ, потому что эта DLL не является частью платформы .NET. В этом случае нам нужно использовать технику позднего связывания. Написание собственных оболочек для вышеупомянутых DLL. И, конечно же, мы сделаем это, потому что это более полезно, чем использование этих DLL. Например, нам не нужно проверять, завершена ли операция загрузки документа, потому что IEHelper сделает это за нас.

csexpert
источник