Wczesne i późne wiązanie

83

Próbuję się dowiedzieć, kiedy w C # występuje wczesne / późne wiązanie.

Metody niewirtualne są zawsze wiązane na wczesnym etapie. Metody wirtualne są zawsze powiązane z opóźnieniem: kompilator wstawia dodatkowy kod, aby rozwiązać rzeczywistą metodę do powiązania w czasie wykonywania i sprawdza bezpieczeństwo typów. Tak więc polimorfizm podtypu wykorzystuje późne wiązanie.

Wywołanie metod przy użyciu odbicia jest przykładem późnego wiązania. Piszemy kod, aby to osiągnąć, a nie kompilator. (Np. Wywołanie komponentów COM).

VB.NET obsługuje niejawne późne wiązanie, gdy opcja Strict jest wyłączona. Obiekt jest wiązany z opóźnieniem, gdy jest przypisany do zmiennej zadeklarowanej jako typu Object. Kompilator VB wstawia kod w celu powiązania z odpowiednią metodą w czasie wykonywania i przechwytywania nieprawidłowych wywołań. C # nie obsługuje tej funkcji.

Czy podążam we właściwym kierunku?

A co z wywoływaniem delegatów i wywoływaniem metody za pośrednictwem odwołania do interfejsu? Czy to wiąże się wcześnie czy późno?

Cybermaxs
źródło

Odpowiedzi:

103

Wszystko jest wcześnie wiązane w C #, chyba że przejdziesz przez interfejs Reflection.

Wczesne wiązanie oznacza po prostu, że metoda docelowa zostanie znaleziona w czasie kompilacji i zostanie utworzony kod, który to wywoła. Niezależnie od tego, czy jest wirtualny, czy nie (co oznacza, że ​​jest dodatkowy krok, aby go znaleźć w czasie rozmowy, nie ma znaczenia). Jeśli metoda nie istnieje, kompilator nie skompiluje kodu.

Późne wiązanie oznacza, że ​​metoda docelowa jest sprawdzana w czasie wykonywania. Często do jej wyszukiwania używana jest tekstowa nazwa metody. Jeśli metody nie ma, bum. Program ulegnie awarii lub przejdzie do jakiegoś schematu obsługi wyjątków w czasie wykonywania.

Większość języków skryptowych używa późnego wiązania, a języki kompilowane używają wczesnego wiązania.

C # (przed wersją 4) nie wiąże się z opóźnieniem; mogą jednak użyć do tego interfejsu API odbicia. Ten interfejs API kompiluje się do kodu, który wyszukuje nazwy funkcji, przeszukując zestawy w czasie wykonywania. VB może późno wiązać, jeśli opcja Strict jest wyłączona.

Wiązanie zwykle wpływa na wydajność. Ponieważ późne wiązanie wymaga wyszukiwania w czasie wykonywania, zwykle oznacza to, że wywołania metod są wolniejsze niż wywołania metod z wczesnym wiązaniem.


W przypadku normalnej funkcji kompilator może obliczyć numeryczne położenie jej w pamięci. Następnie, gdy funkcja jest wywoływana, może wygenerować instrukcję wywołania funkcji pod tym adresem.

Dla obiektu, który ma jakiekolwiek metody wirtualne, kompilator wygeneruje tabelę v-table. Zasadniczo jest to tablica zawierająca adresy metod wirtualnych. Każdy obiekt, który ma metodę wirtualną, będzie zawierał ukryty element członkowski wygenerowany przez kompilator, który jest adresem tabeli v-table. Kiedy wywoływana jest funkcja wirtualna, kompilator ustala, jaka jest pozycja odpowiedniej metody w tabeli v. Następnie wygeneruje kod, który zajrzy do obiektów v-table i wywoła metodę wirtualną w tej pozycji.

Tak więc występuje wyszukiwanie, które występuje dla funkcji wirtualnej. Jest to mocno zoptymalizowane, więc nastąpi to bardzo szybko w czasie wykonywania.

Wczesne wiązanie

  • Kompilator może ustalić, gdzie wywoływana funkcja będzie w czasie kompilacji.
  • Kompilator może zagwarantować wcześnie (przed uruchomieniem kodu programu), że funkcja będzie istnieć i będzie można ją wywołać w czasie wykonywania.
  • Kompilator gwarantuje, że funkcja przyjmuje odpowiednią liczbę argumentów i że są one odpowiedniego typu. Sprawdza również, czy zwracana wartość jest poprawnego typu.

Późne wiązanie

  • Wyszukiwanie potrwa dłużej, ponieważ nie jest to proste obliczenie przesunięcia, zwykle należy dokonać porównań tekstu.
  • Funkcja docelowa może nie istnieć.
  • Funkcja docelowa może nie akceptować przekazanych do niej argumentów i może zwracać wartość niewłaściwego typu.
  • W przypadku niektórych implementacji metoda docelowa może faktycznie ulec zmianie w czasie wykonywania. Tak więc wyszukiwanie może wykonać inną funkcję. Myślę, że dzieje się to w języku Ruby, możesz zdefiniować nową metodę na obiekcie, gdy program jest uruchomiony. Późne wiązanie umożliwia wywołaniom funkcji rozpoczęcie wywoływania nowego zastąpienia metody zamiast wywoływania istniejącej metody podstawowej.
Scott Langham
źródło
Myślę, że chciałeś powiedzieć „Sam język VB nie wiąże się późno…”
Michael Meadows
Właściwie nie używam VB ... więc niewiele o tym wiem. Miałem na myśli C #, ale wygląda na to, że się powtarzałem. Wyobrażam sobie, że masz rację, więc naprawię to!
Scott Langham
21
Dynamiczne pisanie to nie to samo, co późne wiązanie. Różnica jest subtelna, ale ważna. Późne wiązanie nadal wiąże się z typem, po prostu robi to w czasie wykonywania. Dynamiczne pisanie nie wiąże się; zamiast tego rozwiązuje informacje o członkach w czasie wykonywania, niezależnie od typu.
Michael Meadows
1
Dla obiektu, który ma jakiekolwiek metody wirtualne, kompilator wygeneruje tabelę v-table. ”. To trochę niepoprawne - „klasa”, a nie „obiekt”.
turdus-merula
1
@IvanRuski Nie sądzę. W czasie kompilacji wszystkie typy argumentów, które akceptuje delegat, są znane. Tak więc w czasie kompilacji (czyli „wcześnie”), a nie w czasie wykonywania (czyli „późno”), kompilator może zagwarantować, że wywołanie zadziała.
Scott Langham,
18

C # 3 używa wczesnego wiązania.

C # 4 dodaje późne wiązanie ze dynamicsłowem kluczowym. Zobacz wpis na blogu Chrisa Burrowa na ten temat, aby uzyskać szczegółowe informacje.

Jeśli chodzi o metody wirtualne i niewirtualne, to jest inny problem. Jeśli zadzwonię string.ToString(), kod C # jest powiązany z object.ToString()metodą wirtualną . Kod wywołującego nie zmienia się w zależności od typu obiektu. Zamiast tego metody wirtualne są wywoływane za pośrednictwem tabeli wskaźników funkcji. Instancja obiektu odwołuje się do tabeli obiektu wskazując na jego ToString()metodę. Instancja string ma swoją wirtualną tabelę metod wskazującą na jego ToString()metodę. Tak, to jest polimorfizm. ale nie jest to spóźnione wiązanie.

Joe Erickson
źródło
1
Nie zgadzam się całkowicie z tym wyjaśnieniem. W języku C # oznaczanie metody wystąpienia lub pola jako typu pochodnego środków wirtualnych może przesłonić implementację typu podstawowego w łańcuchu dziedziczenia. W przypadku metod wirtualnych środowisko CLR wie, którą metodę wywołać w czasie wykonywania na podstawie wystąpienia obiektu czasu wykonywania. Prawdopodobnie jedyna część, z którą się z tobą zgadzam, to realizacja polimorfizmu. Następnie wprowadzasz zamieszanie, mówiąc, że nie jest to spóźnione wiązanie. Jest to późne wiązanie, ponieważ środowisko CLR może wywołać poprawną implementację typu czasu wykonywania tylko wtedy, gdy zna typ czasu wykonywania wystąpienia obiektu.
Julius Depulla
6

W większości przypadków wczesne wiązanie jest tym, co robimy na co dzień. Na przykład, jeśli mamy Employeeklasę dostępną w czasie kompilacji, po prostu tworzymy instancję tej klasy i wywołujemy jej członków. To jest wczesne wiązanie.

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

Z drugiej strony, jeśli nie masz wiedzy o klasie w czasie kompilacji, jedynym sposobem jest późne powiązanie za pomocą refleksji. Natknąłem się na doskonały film wyjaśniający te pojęcia - oto link .

Prasad
źródło
3

To bardzo stary post, ale chciałem dodać do niego więcej informacji. Późne wiązanie jest używane, gdy nie chcesz tworzyć instancji obiektu w czasie kompilacji. W C#użyć Activatorzadzwonić powiązania obiektu przy starcie.

Kumar Nitesh
źródło
3

Wczesne wiązanie

Sama nazwa opisuje, że kompilator wie, jaki to jest obiekt, jakie są wszystkie zawarte w nim metody i właściwości. Zaraz po zadeklarowaniu obiektu .NET Intellisense wypełni jego metody i właściwości po kliknięciu przycisku z kropką.

Typowe przykłady:

ComboBox cboItems;

ListBox lstItems; W powyższych przykładach, jeśli wpiszemy cboItem i umieścimy kropkę, po której następuje, automatycznie wypełni on wszystkie metody, zdarzenia i właściwości pola kombi, ponieważ kompilator już wie, że to combobox.

Późne wiązanie

Sama nazwa opisuje, że kompilator nie wie, jakiego rodzaju jest to obiekt, jakie są wszystkie zawarte w nim metody i właściwości. Musisz zadeklarować go jako obiekt, później musisz uzyskać typ obiektu, metody, które są w nim przechowywane. Wszystko będzie znane w czasie wykonywania.

Typowe przykłady:

Object objItems;

objItems = CreateObject ("DLL lub nazwa zestawu"); Tutaj w czasie kompilacji typ objItems nie jest określany. Tworzymy obiekt dll i przypisujemy go do objItems, więc wszystko jest ustalane w czasie wykonywania.

Wczesne wiązanie vs. późne wiązanie

Teraz dochodzę do tematu…

Aplikacja będzie działać szybciej we wczesnym wiązaniu, ponieważ nie ma tutaj żadnego pakowania ani rozpakowywania.

Łatwiej jest napisać kod we wczesnym wiązaniu, ponieważ intelisense zostanie automatycznie wypełniony

Minimalne błędy we wczesnym wiązaniu, ponieważ składnia jest sprawdzana podczas samego czasu kompilacji.

Późne wiązanie będzie obsługiwane we wszystkich wersjach, ponieważ wszystko jest ustalane w czasie wykonywania.

Minimalny wpływ kodu na przyszłe ulepszenia, jeśli jest używane późne wiązanie.

Wydajność będzie oznaczona kodem we wczesnym wiązaniu. Oba mają zalety i wady, decyzja programisty o wyborze odpowiedniego wiązania na podstawie scenariusza.

RajGanesh
źródło
2

Mówiąc bardzo prosto, wczesne wiązanie ma miejsce w czasie kompilacji, a kompilator ma wiedzę o typie i wszystkich jego elementach członkowskich, a późne wiązanie ma miejsce w czasie wykonywania, kompilator nie wie nic o typie i jego elementach członkowskich. Natrafiłem na doskonały film na youtube, który wyjaśnia te pojęcia.

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

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

Jasne
źródło
1

Ten artykuł jest przewodnikiem po tworzeniu komponentu .net, używaniu go w projekcie Vb6 w czasie wykonywania przy użyciu późnego wiązania, dołączaniu jego zdarzeń i otrzymywaniu wywołania zwrotnego.

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

Ten artykuł jest przewodnikiem po tworzeniu komponentu .NET i używaniu go w projekcie VB6. Istnieje wiele przykładów dotyczących tego problemu, więc dlaczego napisałem nowy? Moim skromnym zdaniem w innych artykułach brakującą częścią jest dołączenie zdarzenia w czasie wykonywania. Dlatego w tym artykule utworzymy komponent .NET, oznaczymy go jako widoczny komponent COM, użyjemy go w czasie wykonywania w VB6 i dołączymy do jego zdarzeń.

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

Większość programistów często potrzebuje automatyzacji Internet Explorera, co zasadniczo oznacza otwarcie przeglądarki, wypełnienie niektórych formularzy i programowe publikowanie danych.

Najpopularniejszym podejściem jest użycie shdocvw.dll (formantu przeglądarki sieci Web firmy Microsoft) i Mshtml.dll (składnika analizowania i renderowania HTML) lub Microsoft.Mshtml.dll, który jest w rzeczywistości opakowaniem .NET dla Mshtml.dll. Więcej informacji na temat programu Internet Explorer - Informacje o przeglądarce można znaleźć tutaj.

Jeśli wybierzesz powyższą metodę i biblioteki DLL, zobaczmy niektóre z problemów, z którymi możesz mieć do czynienia:

Musisz rozprowadzić te biblioteki DLL, ponieważ projekt byłby zależny od tych bibliotek DLL, a jest to poważny problem, jeśli nie można ich poprawnie wdrożyć. Wystarczy poszukać w Google informacji o problemach z dystrybucją shdocvw i mshtml.dll, a zobaczysz, o czym mówię. Należy wdrożyć plik Microsoft.mshtml.dll o wielkości 8 MB, ponieważ ten plik DLL nie jest częścią środowiska .NET. W tym przypadku musimy użyć techniki późnego wiązania. Pisanie własnych opakowań dla wyżej wymienionych bibliotek DLL. I oczywiście zrobimy to, ponieważ jest to bardziej przydatne niż używanie tych bibliotek DLL. Na przykład nie musimy sprawdzać, czy operacja pobierania dokumentu została zakończona, ponieważ IEHelper zrobi to za nas.

csexpert
źródło