Most script languages use late binding and most compiled languages use early binding; C#, although a compiled language and thus an early binding one, has reflection for late binding. In this post we will explore early & late binding in C# with theory and samples.
A binding is called an early binding, compiler time binding or static type binding when the target method is found during compile time (the code that will call the method is also created during compile time). If the required method doesn’t exist, an error is issued during compilation.
Whether there is an extra step to find the method during call time is irrelevant. That means, the binding is still considered early independently of if the method is virtual or not.
For example, the next code block uses early binding to call the
RandomMethod method of the object
EarlyBindingSample, and then the method
NotExistingMethod that doesn’t exist. Since the second method doesn’t exists, the compiler throws an error:
Late binding or runtime binding in C# is achieved with reflection. Late bound means the target method is looked up at run time. Often the textual name of the method is used to look it up. If the method isn’t there, the program will crash or go into some exception handling scheme during run time.
For example, the code bellow uses reflection to locate and execute two methods, one existing and one not. Since the object returned by the activator is converted to
ILateBoundSample, the compiler knows that the method
NotExistingMethod doesn’t exist. On the other hand the compiler knows there is a method with the
RandomMethod, but that doesn’t mean it knows what is happening inside the loaded assembly. If the loaded type is an implementation of a different version of the
ILateBoundSample the method might not exist.
In the following example though, we use reflection without any conversion, so the compiler never knows what is happening, it just assumes we know what we are doing. The last part of the following code will produce a runtime error:
A step deeper in late binding
Late Binding usually has an impact on performance because late binding requires lookups at runtime, thus, the developer has to choose which stage will be impacted more by binding; it is usually compile time that is chosen. The runtime delay though, although it’s there, is not that big in most of the cases:
- For a normal function, the compiler can work out the numeric location of it in memory; when the function is called it can generate an instruction to call the function at this address.
- For an object that has any virtual methods, the compiler will generate an array that contains the addresses of the virtual methods; this array is called a VMT or a Virtual Method Table. Additionally, again during compile time, the compiler will also generate a hidden member for that object, which contains the address of the VMT previously created. When a virtual function is called, the compiler will work out what the position is of the appropriate method in the VMT. It will then generate code to look in the objects VMT and call the virtual method at this position.
The lookup happening for virtual methods is heavily optimized so it will happen very quickly during runtime. That doesn’t mean there isn’t any overhead though, so when performance is super critical either early binding is chosen (or at least late bind caching during startup).
And what about
Introduced in C# 4, many consider
dynamic typing as late binding but it is not!
Usually programming languages are either dynamic typed or strongly typed and C# -as we know- is strongly typed. Type dynamic was introduced in C#4 as an addition that comes to join the two worlds by resolving information at runtime irrespective of type. This is not binding at all, because it never binds to a type.
Read how to use type dynamic in Microsoft Docs
The playground I used during writing this article can be found in my github account with the repo name LateBindingHelloWorld. It is just a minimalistic approach on how to dynamically load and run code from an external assembly, resembling a real life scenario of dynamic module loading.