Translator's Preface 
This is more a free retelling, not a translation. I included in this article only those parts of the original that are directly related to the internal mechanisms of DLR or explain important ideas. Notes will be enclosed in square brackets.Many .NET developers have heard of Dynamic Language Runtime (DLR), but know almost nothing about it. Developers writing in languages such as C # or Visual Basic avoid dynamic typing languages for fear of historically related scalability issues. They are also concerned about the fact that languages like Python or Ruby do not perform type checking at compile time, which can lead to runtime errors that are difficult to find and fix. These are well-founded fears that may explain why DLR is not popular among the majority of .NET developers even two years after the official release 
[the article is quite old, but nothing has changed since then] . After all, any .NET 
Runtime containing the words 
Dynamic and 
Language in its name should be designed strictly to support languages such as Python, right?
Slow down. While DLR was really designed to support the Iron implementation of Python and Ruby in the .NET Framework, its architecture provides much deeper abstractions.

Under the hood, DLR offers a rich set of interfaces for inter-process communication [Inter-Process Communication (IPC)]. Over the years, developers have seen many Microsoft tools for interaction between applications: DDE, DCOM, ActiveX, .Net Remoting, WCF, OData. This list may go on for a long time. This is an almost endless parade of acronyms, each of which represents a technology that promises that this year it will be even easier to exchange data or call remote code than before.
Language of languages
The first time I heard Jim Hugunin talk about DLR, his speech surprised me. Jim created a Python implementation for the Java Virtual Machine (JVM) known as Jython. Shortly before the show, he joined Microsoft to create IronPython for .NET. Based on his background, I expected him to focus on the language, but instead, Jim talked almost all the time about abstruse things like expression trees, dynamic call dispatch, and call caching mechanisms. Jim described a set of runtime compilation services that allowed any two languages to interact with each other with virtually no loss in performance.
During this speech, I wrote down a term that surfaced in my head when I heard Jim retelling the DLR architecture: the language of languages. Four years later, this nickname still characterizes DLR very accurately. However, having gained real-world experience in using it, I realized that DLR is not just about language compatibility. Thanks to the support of dynamic types in C # and Visual Basic, DLR can act as a gateway from our favorite .NET languages to data and code in any remote system, regardless of what type of equipment or software the latter uses.

To understand the idea behind DLR, which is an integrated mechanism in the IPC language, let's start with an example that has nothing to do with dynamic programming. Imagine two computer systems: one called the initiator, and the second - the target system. The initiator needs to execute the 
foo function on the target system, passing there a certain set of parameters, and get the results. After the target system is discovered, the initiator must provide all the information necessary for the execution of the function in a format that is clear to her. At a minimum, this information will include the name of the function and the parameters passed. After unpacking the request and validating the parameters, the target system will execute the foo function. After that, it should pack the result, including any errors that occurred during execution, and send them back to the initiator. Finally, the initiator should be able to unpack the results and notify the goal. This request-response pattern is quite common and at a high level describes the operation of almost any IPC mechanism.
Dynamicmetaobject
To understand how DLR implements the presented pattern, let's look at one of the central classes of DLR: 
DynamicMetaObject . We start by exploring three of the twelve key methods of this type:
- BindCreateInstance - create or activate an object
- BindInvokeMember - call the encapsulated method
- BindInvoke - object execution (as a function)
When you need to execute a method on a remote system, you first need to create an instance of the type. Of course, not all systems are object-oriented, so the term instance can be a metaphor. In fact, the service we need can be implemented as a pool of objects or as a singleton, so the terms “activation” or “connection” can be used with the same right as “instance”.
Other frameworks follow the same pattern. For example, COM provides a 
CoCreateInstance function for creating objects. In .NET Remoting, you can use the 
CreateInstance method from the 
System.Activator class. DLR 
DynamicMetaObject provides a 
BindCreateInstance for similar purposes.
After using the 
BindCreateInstance method, 
something created can be a type that exposes several methods. The 
BindInvokeMember metaobject 
method is used to bind an operation that can call a function. In the picture above, the string foo can be passed as a parameter to indicate to the binder that a method with that name should be called. Additionally included is information about the number of arguments, their names and a special flag that indicates to the binder whether it is possible to ignore case when searching for a suitable named element. After all, not all languages are case sensitive.
When 
something returned from 
BindCreateInstance is just one function (or delegate), the BindInvoke method is used. To clarify the picture, let's look at the following small piece of dynamic code:
delegate void IntWriter(int n); void Main() { dynamic Write = new IntWriter(Console.WriteLine); Write(5); } 
This code is not the best way to print the number 5 to the console. A good developer will never use anything so wasteful. However, this code illustrates the use of a dynamic variable whose value is a delegate that can be used as a function. If the delegate type implements the 
IDynamicMetaObjectProvider interface, then the 
BindInvoke method from 
DynamicMetaObject will be used to bind the operation to the real work. This is because the compiler recognizes that the dynamic 
Write object is 
syntactically used as a function. Now consider another piece of code to understand when the compiler will generate 
BindInvokeMember :
 class Writer : IDynamicMetaObjectProvider { public void Write(int n) { Console.WriteLine(n); }  
I will omit the implementation of the interface in this small example because it will take a lot of code to demonstrate this correctly. In this shortened example, we implement a dynamic meta object with just a few lines of code.
An important thing to understand is that the compiler recognizes that 
Writer.Write (7) is an element access operation. What we usually call the "dot operator" in C # is formally called the "type member access operator". The DLR code generated by the compiler in this case will eventually call 
BindInvokeMember , into which it will pass the string Write and parameter number 7 to the operation that is capable of making the call. In short, 
BindInvoke is used to call a dynamic object as a function, while 
BindInvokeMember is used to call a method as an element of a dynamic object.
Access properties through DynamicMetaObject
It can be seen from the above examples that the compiler uses the language syntax to determine which DLR binding operations should be performed. If you use Visual Basic to work with dynamic objects, then its semantics will be used. The access operator (dot), of course, is needed not only to access methods. You can use it to access properties. The DLR meta object provides three methods for accessing the properties of dynamic objects:
- BindGetMember - get property value
- BindSetMember - set the value of a property
- BindDeleteMember - delete an item
The purpose of 
BindGetMember and 
BindSetMember should be obvious. Especially now that you know how they relate to how .NET works with properties. When the compiler calculates the 
get ("read") properties of a dynamic object, it uses a call to 
BindGetMember . When the compiler computes set ("record"), it uses 
BindSetMember .
Representation of an object as an array
Some classes are containers for instances of other types. DLR knows how to handle such cases. Each “array-oriented” meta-object method has an “Index” postfix:
- BindGetIndex - get value by index
- BindSetIndex - set value by index
- BindDeleteIndex - delete a value by index
To understand how 
BindGetIndex and 
BindSetIndex are used , imagine a 
JavaBridge wrapper 
class that can load files with Java classes and allows you to use them from .NET code without too much difficulty. Such a wrapper can be used to load the 
Customer Java class, which contains some ORM code. The DLR meta object can be used to call this ORM code from .NET in the classic C # style. Below is sample code that shows how 
JavaBridge can work in practice:
 JavaBridge java = new JavaBridge(); dynamic customers = java.Load("Customer.class"); dynamic Jason = customers["Bock"]; Jason.Balance = 17.34; customers["Wagner"] = new Customer("Bill"); 
Since the third and fifth lines use the access operator by index ([]), the compiler recognizes this and uses the 
BindGetIndex and 
BindSetIndex methods when working with the meta object returned from 
JavaBridge . It is understood that the implementation of these methods on the returned object will request the execution of the method from the JVM through the Java Remote Method Invocation (RMI). In this scenario, DLR acts as a bridge between C # and another language with static typing. Hopefully this clarifies why I called DLR “language of languages”.
The 
BindDeleteMember method, just like 
BindDeleteIndex , is not intended for use from languages with static typing such as C # and Visual Basic, since they do not support the concept itself. However, you can agree to consider “removal” some operation expressed by the means of the language, if it is useful to you. For example, you can implement BindDeleteMember as nulling an element by index.
Transforms and Operators
The last group of DLR meta-object methods is about handling operators and transformations.
- BindConvert - convert an object to another type
- BindBinaryOperation - using a binary operator on two operands
- BindUnaryOperation - applying a unary operator on one operand
The 
BindConvert method 
is used when the compiler realizes that the object needs to be converted to another known type. Implicit conversion occurs when the result of a dynamic call is assigned to a variable with a static type. For example, in the following C # example, assigning the variable 
y leads to an implicit call to 
BindConvert :
 dynamic x = 13; int y = x + 11; 
The 
BindBinaryOperation and 
BindUnaryOperation methods are always used when arithmetic operations ("+") or increments ("++") are encountered. In the example above, adding dynamic variable 
x to constant 11 will call the 
BindBinaryOperation method. Remember this little example, we use it in the next section to bang another key DLR class called CallSite.
Dynamic dispatch with CallSite
If your introduction to DLR did not go beyond using the 
dynamic keyword, then you probably would never have known about the existence of CallSite in the .NET Framework. This modest type, formally known as 
CallSite < T > , resides in the 
System.Runtime.CompilerServices namespace . This is the “power source” of metaprogramming: it is filled with all sorts of optimization methods that make dynamic .NET code fast and efficient. I will mention 
CallSite < T > performance aspects at the end of the article.
Most of what CallSite does in dynamic .NET code involves generating and compiling code in runtime. It is important to note that the 
CallSite < T > class lies in the namespace that contains the words " 
Runtime " and " 
CompilerServices ". If DLR is a "language of languages", then 
CallSite < T > is one of its most important grammatical constructions. Let's look at our example from the previous section again to get to know CallSite and how the compiler embeds them in your code.
 dynamic x = 13; int y = x + 11; 
As you already know, the 
BindBinaryOperaion and 
BindConvert methods will be invoked to execute this code. Instead of showing you a long listing of disassembled MSIL code generated by the compiler, I put together a diagram:

Remember that the compiler uses the language syntax to determine which dynamic type methods to execute. In our example, two operations are performed: adding the variable 
x to the number ( 
Site2 ) and casting the result to int ( 
Site1 ). Each of these actions turns into CallSite, which is stored in a special container. As you can see in the diagram, CallSites are created in the reverse order, but are called in the right way.
In the figure you can see that the metaobject methods 
BindConvert and 
BindBinaryOperation are called immediately before the operations “create CallSite1” and “create CallSite2”. However, bound operations are only performed at the very end. I hope the visualization helps you to understand that binding methods and calling them are different operations in the context of DLR. Moreover, binding occurs only once, while a call occurs as many times as needed, reusing already initialized CallSites to optimize performance.
Follow the easy way
At the very heart of DLR, expression trees are used to generate functions tied to the twelve binding methods presented above. Many developers are constantly confronted with expression trees using LINQ, but only a few have deep enough experience to fully implement the 
IDynamicMetaObjectProvider contract. Fortunately, the .NET Framework contains a base class called 
DynamicObject , which takes care of most of the work.
To create your own dynamic class, you just need to inherit from 
DynamicObject and implement the following twelve methods:
- TryCreateInstance
- TryInvokeMember
- Tryinvoke
- TryGetMember
- TrySetMember
- TryDeleteMember
- TryGetIndex
- TrySetIndex
- TryDeleteIndex
- Tryconvert
- TryBinaryOperation
- TryUnaryOperation
Do the method names look familiar? You must, because you just finished studying the elements of the Abstract 
DynamicMetaObject class, which include methods like 
BindCreateInstance and 
BindInvoke . The 
DynamicMetaObject class provides an implementation for 
IDynamicMetaObjectProvider , which returns 
DynamicMetaObject from its only method. The operations associated with the base implementation of the meta object simply delegate their calls to methods starting with “Try” on the 
DynamicObject instance. All you have to do is to overload methods like 
TryGetMember and 
TrySetMember in a class inherited from 
DynamicObject , while the meta object will take on all the dirty work with expression trees.
Caching
[You can read more about caching in my previous DLR article ]The greatest concern when working with dynamic languages for developers is performance. DLR takes extraordinary measures to dispel these experiences. I briefly mentioned the fact that 
CallSite < T > resides in a namespace called 
System.Runtime.CompilerServices . In the same namespace lies several other classes that provide multilevel caching. Using these types, DLR implements three main levels of caching to speed up dynamic operations:
- Global cache
- Local cache
- Polymorphic Delegate Cache
The cache is used in order to avoid unnecessary waste of resources for creating bindings for a specific CallSite. If two objects of type 
string are passed to a dynamic method that returns 
int , then the global or local cache will save the resulting binding. This will greatly simplify subsequent calls.
The delegate cache, which is located inside CallSite itself, is called polymorphic, because these delegates can take different forms depending on which dynamic code is executed and which rules from other caches were used to generate them. The delegate cache is also sometimes called the inline cache. The reason for using this term is that the expressions generated by DLR and their binders are converted to MSIL code that goes through JIT compilation like any other .NET code. Compilation at runtime occurs simultaneously with the “normal” execution of your program. It is clear that turning on-the-fly dynamic code into compiled MSIL code during program execution can greatly affect application performance, so caching mechanisms are vital.