IDynamicMetaObjectProvider IDynamicMetaObjectProvider is an important interface in the dynamic world that represents an object that has operations bound at runtime.. You can use this in
Trang 1public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
string nodeName = binder.Name;
result = _xml.Element("test").Element(nodeName).Value;
return true;
}
}
}
In this example, the TryGetMember() method intercepts the call to node1 and node2, thus allowing
us to query the XML document and return the individual nodes
IDynamicMetaObjectProvider
IDynamicMetaObjectProvider is an important interface in the dynamic world that represents an object that has operations bound at runtime Both ExpandoObject and DynamicObject implement this interface You
can use this interface to add dynamic functionality to your own classes IDynamicMetaObjectProvider
requires you to implement GetMetaObject(),(), which resolves binding operations (for example, method or property invocation on your object)
Dynamic Limitations
When working with dynamic objects, there are a number of constraints you should be aware of:
• All methods and properties in classes have to be declared public to be dynamically
accessible
• You cannot use the DLR to create classes in C# or VB.NET Apparently, the DLR
does allow you to create classes, but this cannot be expressed using C# or VB.NET
• Dynamic objects cannot be passed as arguments to other functions
• Extension methods cannot be called on a dynamic object and a dynamic object
cannot be passed into extension objects
Annoyingly, these restrictions stop you calling an extension method on a dynamic object
Dynamic IL
You may be wondering what code the C# compiler generates when you use the dynamic keyword Let’s
take a look at the IL that is generated using ILDASM for a simple console application that declares and
initializes a string:
string d;
d = "What do I look like in IL";
Trang 2This will generate the following IL:
.method private hidebysig static void Main(string[] args) cil managed
{
entrypoint
// Code size 8 (0x8)
maxstack 1
locals init ([0] string d)
IL_0000: nop
IL_0001: ldstr "What do I look like in IL"
IL_0006: stloc.0
IL_0007: ret
} // end of method Program::Main
Now we will alter d to be of type dynamic:
dynamic d;
d = ”What do I look like in IL?”;
This will generate the following IL:
.method private hidebysig static void Main(string[] args) cil managed
{
entrypoint
// Code size 8 (0x8)
maxstack 1
locals init ([0] object d)
IL_0000: nop
IL_0001: ldstr "What do I look like in IL"
IL_0006: stloc.0
IL_0007: ret
} // end of method Program::Main
Note how the line locals init ([0] object d) replaces locals init ([0] string d) in the dynamic example This is probably what you might expect to happen, but let’s take another more complex example Create a new console application called Chapter3.DynamicComplex and add the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Chapter3.DynamicComplex
{
class Program
{
static void Main(string[] args)
{
TestClass t = new TestClass();
t.Method1();
Trang 3public class TestClass
{
public void Method1() { }
}
}
Compile the application and examine the IL using ILDASM You will find something similar to the following:
.method private hidebysig static void Main(string[] args) cil managed
{
entrypoint
// Code size 15 (0xf)
maxstack 1
locals init ([0] class Chapter3.DynamicComplex.TestClass t)
IL_0000: nop
IL_0001: newobj instance void Chapter3.DynamicComplex.TestClass::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance void Chapter3.DynamicComplex.TestClass::Method1()
IL_000d: nop
IL_000e: ret
} // end of method Program::Main
However, if we alter our t variable to the following:
dynamic t = new TestClass();
t.Method1();
then the IL will look very different (I have removed some of the IL to save some trees):
class [mscorlib]System.Collections.Generic.IEnumerable`1<class
[Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_003a: call class [System.Core]
System.Runtime.CompilerServices.CallSite`1<!0> class
[System.Core]System.Runtime.CompilerServices.CallSite`1
<class [mscorlib]System.Action`2<class
[System.Core]System.Runtime.CompilerServices.CallSite,object>>::Create(class
[System.Core]System.Runtime.CompilerServices.CallSiteBinder)
IL_003f: stsfld class [System.Core]System.Runtime.CompilerServices
.CallSite`1<class [mscorlib]System.Action`2<class
[System.Core]System.Runtime.CompilerServices.CallSite,object>>
Chapter3.DynamicComplex.Program/'<Main>o SiteContainer0'::'<>p Site1'
IL_0056: callvirt instance void class [mscorlib]System.Action`2<class
[System.Core]System.Runtime.CompilerServices.CallSite,object>::Invoke(!0, !1)
Whoa, what is happening here? Well the short answer is that calls to dynamic methods are sent to
the Dynamic Language Runtime for resolution It is time to take a look into how the DLR works
Trang 4Dynamic Language Runtime (DLR)
The Dynamic Language Runtime (DLR) is behind all the cool dynamic functionality and sits just above the core NET framework The DLR’s job is basically to resolve calls to dynamic objects, cache dynamic calls making them as quick as possible, and enable interaction between languages by using a common format The DLR has actually been around a while, and was included in earlier versions of Silverlight You can even view the source code behind the DLR at: http://dlr.codeplex.com Note that this version contains a number of features not present in the framework version
When discussing the DLR we need to understand five main concepts:
• Expression trees/Abstract Syntax Trees (AST)
• Dynamic Dispatch
• Binders
• IDynamicObject
• Call Site Caching
Expression/Abstract Syntax Trees (AST)
Expression trees are a way of representing code in a tree structure (if you have done any work with LINQ, you may have come across this before with the Expression class) All languages that work with the DLR represent code in the same structure allowing interoperability
Dynamic Dispatch
Dynamic Dispatch is the air traffic control center of the DLR, and is responsible for working out what to
do with dynamic objects and operations and sending them to the appropriate binder that takes care of the details
Binders
Binders resolve classes from dynamic dispatch .NET 4.0 currently supports the following binder types:
• Object Binder NET (uses Reflection and resolved our earlier example to type string)
• JavaScript binder (IDynamicObject)
• IronPython binder (IDynamicObject)
• IronRuby binder (IDynamicObject)
• COM binder (IDispatch)
Note that dynamic objects can resolve calls themselves without the DLR’s assistance (if they implement IDynamicObject), and this method will always be used first over the DLR’s dynamic dispatch mechanism
Trang 5IDynamicObject
Sometimes you will want objects to carry out resolution themselves, and it is for this purpose the
IDynamicObject exists Normally dynamic objects are processed according to type, but if they implement the IDynamicObject interface then the object will resolve calls itself IDynamicObject is used in IronRuby and IronPython
Callsite Caching
Resolving objects is an expensive operation, so the DLR caches dynamic operations When a dynamic
function or operation is performed, the DLR checks to see if it has been called already (Level 0 cache) If
it hasn’t, then the 10 most recently used dynamic methods for this callsite will be checked (Level 1
cache) A cache is also maintained across all target sites with the same binder object (Level 2 cache)
IronPython
A similar process to this is used when languages such as IronPython interact with NET What follows is a high-level version of how the DLR processes an IronPython file:
1 The IronPython file is first compiled into intermediary IronPython AST (Not all languages will necessarily create an intermediary AST, but IronPython’s developers decided this would be a
useful step for creating language-specific tools.)
2 The IronPython AST is then mapped to the generic DLR specific AST
3 The DLR then works with the generic AST
For a detailed look at how this works with Iron Python please refer to: http://msdn.microsoft.com/ en-us/magazine/cc163344.aspx
As all languages end up being compiled into the same common AST structure, it is then possible for interaction between them
Embedding Dynamic Languages
One use of dynamic languages that really excites me is the ability to embed them within your C# and
VB.NET applications One possible use would be to use them to define complex business rules and logic
Dynamic languages are often utilized in computer game construction to script scenarios and logic (such as how Civilization IV utilizes Python) Let’s take a look at how to work with IronPython in a C# application
Calling IronPython from NET
The following example passes a value into a simple IronPython script from C# Note that you should
have installed IronPython from http://ironpython.codeplex.com/ Now add a reference to
IronPython.dll and Microsoft.Scripting.dll (at the time of writing these don’t show up on the main
Add Reference window but are located at C:\Program Files (x86)\IronPython 2.6)
Trang 6using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
namespace Chapter3.PythonExample
{
class Program
{
static void Main(string[] args)
{
ScriptEngine pythonEngine = Python.CreateEngine();
ScriptScope scope = pythonEngine.CreateScope();
string script = @"print ""Hello "" + message";
scope.SetVariable("message", "world!");
ScriptSource source =
scope.Engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements); source.Execute(scope);
Console.ReadKey();
}
}
}
IronPython is already in use in two real-world applications, so let’s take a look at these now
Red Gate Reflector Add-In
Many of you will be familiar with the tool Reflector (www.red-gate.com/products/reflector/) Reflector allows you to explore an assembly and view the IL code within it C# MVP Ben Hall developed an add-in (Methodist) to Reflector that allows you to actually call the classes and methods within the type you are exploring using Iron Python For more information please consult: http://mail.simple-talk.com/ dotnet/.net-tools/methodist-make-.net-reflector-come-alive-with-ironpython/
ResolverOne
One of the best know uses of IronPython is for ResolverOne (http://www.resolversystems.com)
ResolverOne is an application that allows you to work with Excel’s objects using IronPython See Figure 3-3