ダイナミックバインディング

動的バインディング（英: dynamic binding）、遅延バインディング（英: late binding）あるいは動的リンケージ（英: dynamic linkage）は、コンピュータプログラミングにおいて、オブジェクトに対して呼ばれるメソッドあるいは引数を伴い呼び出される関数を、その名前に基づいて実行時に探索 (look up) するというメカニズムである。それぞれ動的束縛、遅延束縛、動的結合とも邦訳される。しかし、動的ディスパッチ（英語版）の意味でこれらの用語が使われることもある。 対義語は静的バインディング（英: static binding）あるいは事前バインディング（英: early binding）であり、コンパイル時に確定する静的なオフセット情報などをもとにして関数実体のアドレスを取得する。 静的型付けのプログラミング言語において、変数や式の型はコンパイル時に確定する。関数（メソッド）の存在確認は、コンパイル時にその名前（識別子）や引数リストの型、所属する名前空間および型などに基づいて事前に検査される。プログラマによって記述された関数呼び出しを、実際に関数が実装されているコードに一致させるプロセスのことをバインディングと呼ぶ。 C++やJavaといった静的型付けのオブジェクト指向言語では、オブジェクトの型に基づいて実際に呼び出される関数（メソッド）が実行時に選択される「仮想関数」（仮想メソッド）を使った動的ディスパッチ（英: dynamic dispatch）によりポリモーフィズムを実現する。コンパイル時には、所望の関数を呼び出すためのアドレス情報が（バイナリ）プログラム内に仮想関数テーブルにおけるメモリオフセットとして保持される。実行時に格納されたメモリオフセットすなわちアドレス情報（関数ポインタ）を使って所望の関数が呼び出される。この方式は、仮想関数テーブルの構造が事前に分かっているという点で、事前バインディングの一種である。 遅延バインディングでは、コンパイラは仮想関数テーブルに所望の仮想関数が存在するかどうかを確認するために十分な情報を読み取らず、代わりに名前を用いて実行時にルックアップする。Component Object Model (COM) プログラミングにおける遅延バインディングの主要な利点は、コンパイラがオブジェクトの型情報を含むライブラリ（DLL）を参照する必要がないということである。名前ベースの柔軟なプログラミングが可能となることで、ライブラリのバージョン衝突に対してコンパイル処理に耐性がもたらされる。遅延バインディングのデメリットとして、実行時に名前解決を試みるために、統合開発環境によるコード補完などの支援や、コンパイル時の静的な型チェック機構が働かず、実行時になって初めてプログラミングミスが発覚する可能性が高くなること、またコンパイラ最適化が働かず、オーバーヘッドが大きくなり実行速度が低下することが挙げられる。 動的プログラミング言語のシンボル探索は基本的に名前ベースで、実行時に解決する動的バインディング方式である。ダック・タイピングも参照のこと。 C++では以下のように仮想関数を派生クラスにてオーバーライドすることで、実際に呼び出される関数の実体をオブジェクトの型に応じて実行時に選択することができる。これを動的ディスパッチと呼ぶ。 #include <iostream> class Base { public: // 基底クラスにて virtual 指定を外すと、動的ディスパッチではなくなる。 virtual void MemberFunc() { std::cout << "Base::MemberFunc() is called." << std::endl; } }; class Derived : public Base { public: // 派生クラスでの virtual 指定の有無は、オーバーライドとは無関係。 // C++11 規格以降は override キーワードを後置することで、正しくオーバーライドされているかどうかコンパイラにチェックさせることもできる。 virtual void MemberFunc() { std::cout << "Derived::MemberFunc() is called." << std::endl; } }; // Base クラスから派生していない型のインスタンスを Base& で受け取ることはできない。コンパイラの静的な型チェックによりエラーとなる。 // つまり、MemberFunc() の存在有無は静的に検証される。 void CallMemberFunc(Base& obj) { obj.MemberFunc(); } int main() { Base b; CallMemberFunc(b); Derived d; CallMemberFunc(d); // Base& で受け取って MemberFunc() を呼び出しているが、実際に呼ばれるのは Derived::MemberFunc() のほう。 } 仮想関数テーブルを利用した動的ディスパッチは、前述のように事前バインディングの一種であり、真の意味での動的バインディング／遅延バインディングとは異なる。しかし、動的ディスパッチの意味でこれらの用語が使われていることもある。 VB.NETにおける遅延バインディングの例を示す。 Option Strict Off Class SomeClass1 Public Sub SomeMethod Console.WriteLine("SomeClass1.SomeMethod() is called.") End Sub End Class Class SomeClass2 Public Sub SomeMethod Console.WriteLine("SomeClass2.SomeMethod() is called.") End Sub End Class Public Class LateBindingExample Shared Sub CallSomeMethod(obj As Object) ' Option Strict On を指定するとコンパイルエラーになる。 obj.SomeMethod() End Sub Shared Function Main As Integer CallSomeMethod(New SomeClass1()) CallSomeMethod(New SomeClass2()) Return 0 End Function End Class VB.NETにおけるObject型は、.NETの基本クラス型System.Objectであり、あらゆる型の最上位基底クラスであるが、Object型自体には上記のSomeMethodという名前を持つメソッドは定義されていない。しかし、Option Strict Offを指定することで、コンパイル時にメソッドの存在有無を確認しなくなり、実行時に探索を試みる。これが遅延バインディングである。もし該当メソッドが見つからなかった場合、実行時に例外System.MissingMemberExceptionがスローされる。 なお、VB.NETのコンパイラでは、デフォルトでOption Strict Offとなっており、遅延バインディングが許可されている。 JavaやC#ではリフレクションを使って遅延バインディングを実現することもできる。C# 4.0以降はdynamic型を使用した遅延バインディングも可能となっている。Visual Basic .NET (VB.NET) ではOption Strict Offによって遅延バインディングが可能である。 Windows APIのGetProcAddress()や、POSIXのdlsym()は、関数名の文字列に基づいてモジュール内の関数ポインタを探索する。これらを使うことで、遅延バインディングを実現することもできる。