Tải bản đầy đủ (.pdf) (6 trang)

Lập trình .net 4.0 và visual studio 2010 part 8 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (100.1 KB, 6 trang )

CHAPTER 3  LANGUAGE AND DYNAMIC CHANGES

55
public 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";

CHAPTER 3  LANGUAGE AND DYNAMIC CHANGES

56
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] 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();
}
}

CHAPTER 3  LANGUAGE AND DYNAMIC CHANGES

57
public 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.
CHAPTER 3  LANGUAGE AND DYNAMIC CHANGES

58
Dynamic 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: . 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.
CHAPTER 3  LANGUAGE AND DYNAMIC CHANGES

59
IDynamicObject
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:
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 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).

CHAPTER 3  LANGUAGE AND DYNAMIC CHANGES

60
using 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:
dotnet/.net-tools/methodist-make net-reflector-come-alive-with-ironpython/.
ResolverOne
One of the best know uses of IronPython is for ResolverOne ().
ResolverOne is an application that allows you to work with Excel’s objects using IronPython. See Figure 3-3.

×