Skip to content

dynamicobject and expandoobject

Stéphane Lozier edited this page Jan 18, 2021 · 1 revision

4 DynamicObject and ExpandoObject

We make life much simpler for library authors who want to create objects in static languages so that the object can behave dynamically and participate in the interoperability and performance of dynamic call sites. Library authors can avoid the full power of DynamicMetaObjects, but they can also employ DynamicMetaObjects if they wish. The DLR provides two higher level abstractions over DynamicMetaObject: DynamicObject and ExpandoObject. For most APIs, these objects provide more than enough performance and flexibility to expose your functionality to dynamic hosts.

4.1 DynamicObject

The simplest way to give your own class custom dynamic dispatch semantics is to derive from the DynamicObject base class. DynamicObject lets your objects fully participate in the dynamic object interoperability protocol, supporting the full set of operations available to objects that provide their own custom DynamicMetaObjects. DynamicObject lets you choose which operations to implement, and allows you to implement them much more easily than a language implementer who uses DynamicMetaObject directly.

DynamicObject provides a set of 12 virtual methods, each corresponding to a Bind... method defined on DynamicMetaObject. These methods represent the dynamic operations others may perform on your objects:

public abstract class DynamicObject : IDynamicMetaObjectProvider {

public virtual bool TryGetMember(GetMemberBinder binder,

out object result)

public virtual bool TrySetMember(SetMemberBinder binder,

object value)

public virtual bool TryDeleteMember(DeleteMemberBinder binder)

public virtual bool TryConvert(ConvertBinder binder,

out object result)

public virtual bool TryUnaryOperation

(UnaryOperationBinder binder, out object result)

public virtual bool TryBinaryOperation

(BinaryOperationBinder binder, object arg,

out object result)

public virtual bool TryInvoke

(InvokeBinder binder, object[] args, out object result)

public virtual bool TryInvokeMember

(InvokeMemberBinder binder, object[] args,

out object result)

public virtual bool TryCreateInstance

(CreateInstanceBinder binder, object[] args,

out object result)

public virtual bool TryGetIndex

(GetIndexBinder binder, object[] args, out object result)

public virtual bool TrySetIndex

(SetIndexBinder binder, object[] indexes, object value)

public virtual bool TryDeleteIndex

(DeleteIndexBinder binder, object[] indexes)

You could have your own TryGetMember implementation look up “Foo” in a dictionary, crawl through a dynamic model like XML, make a web request for a value, or some other custom operation. To do so, you would override the TryGetMember method and just implement whatever custom action you want to expose through member evaluation syntax. You return true from the method to indicate that your implementation has handled this situation, and supply the value you want returned as the out parameter, result.

By default, the methods that you don’t override on DynamicObject fall back to the language binder to do binding, offering no special behavior themselves. For example, let’s say you have a class, MyClass, derived from DynamicObject that does not override TryGetMember. You also have an instance of MyClass in a variable, myObject, of type C# 'dynamic'. If you evaluate myObject.Foo, the evaluation falls back to C#’s runtime binder, which will simply look for a field or property named Foo (using .NET reflection) defined as a member of MyClass. If there is none, the C# binder will store a binding in the cache that will throw a runtime binder exception in this situation. A more real example is that you do override TryGetMember to look in your dictionary of dynamic members, and you return false if you have no such members. The DynamicMetaObject for MyClass produces a rule that first looks for static members on MyClass, then calls TryGetMember which may return false, and finally throws a language-specific exception when it finds no members.

In the full glory of the interoperability protocol, a dynamic object implements IDynamicMetaObjectProvider and returns a DynamicMetaObject to represent the dynamic view of the object at hand. The DynamicMetaObject looks a lot like DynamicObject, but its methods have to return Expression Trees that plug directly into the DLR's dynamic caching mechanisms. This gives you a great deal of power, and the ability to squeeze out some extra efficiency, while DynamicObject gives you nearly the same power in a form much simpler to consume. With DynamicObject, you simply override methods for the dynamic operations in which your dynamic object should participate. The DLR automatically creates a DynamicMetaObject for your DynamicObject. This DynamicMetaObject creates Expression Trees (for the DLR’s caching system) that simply call your overridden DynamicObject methods.

4.2 ExpandoObject

The ExpandoObject class is an efficient implementation of a dynamic property bag provided for you by the DLR. It allows you to dynamically retrieve and set its member values, adding new members per instance as needed at runtime. Because ExpandoObject implements the standard DLR interface IDynamicMetaObjectProvider, it is portable between DLR-aware languages. You can create an instance of an ExpandoObject in C#, give its members specific values and functions, and pass it on to an IronPython function, which can then evaluate and invoke its members as if it was a standard Python object. ExpandoObject is a useful library class when you need a reliable, plain-vanilla dynamic object.

4.3 Further Reading

If you’d like to learn more about DynamicObject and ExpandoObject, check out the accompanying Getting Started with the DLR for Library Authors on the DLR CodePlex site under Specs and Docs. That document covers DynamicObject and ExpandoObject at a deeper level and includes an example of using DynamicMetaObject to optimize a dynamic library.

Sites, Binders, and Dynamic Object Interop Spec

Frontmatter
1 Introduction
  1.1 Performance
  1.2 Language Interop
2 Dynamic Call Sites
  2.1 Rules
  2.2 CallSiteBinder
  2.3 CallSite<T>
    2.3.1 L0 Cache: CallSite’s Target Delegate
    2.3.2 L1 Cache: CallSite’s Rule Set
    2.3.3 L2 Cache: Combined Rule Sets of All Equivalent CallSites
    2.3.4 Other Optimizations
3 IDynamicMetaObjectProvider and DynamicMetaObject
  3.1 IDynamicMetaObjectProvider
  3.2 DynamicMetaObject
  3.3 DynamicMetaObjectBinder
    3.3.1 Fallback Methods – Implementing the Language’s Semantics
    3.3.2 Error Suggestions
  3.4 Dynamic Binding Walkthroughs
    3.4.1 a + b
    3.4.2 a.Foo(b)
4 DynamicObject and ExpandoObject
  4.1 DynamicObject
  4.2 ExpandoObject
  4.3 Further Reading
5 Dynamic Object Conventions
  5.1 Enumerability
  5.2 Disposability
6 API Reference
  6.1 DynamicMetaObject Class
    6.1.1 Class Summary
    6.1.2 Bind**Operation** Methods
    6.1.3 Expression Property
    6.1.4 Restrictions Property
    6.1.5 Value and HasValue Properties
    6.1.6 LimitType Property
    6.1.7 Create Static Method
    6.1.8 GetDynamicMemberNames Method
  6.2 IDynamicMetaObjectProvider Interface
    6.2.1 Class Summary
    6.2.2 GetMetaObject Method
  6.3 CallSiteBinder Abstract Class
    6.3.1 Class Summary
    6.3.2 Bind Method
    6.3.3 BindDelegate Method
    6.3.4 CacheTarget Method
    6.3.5 UpdateLabel Static Property
  6.4 DynamicMetaObjectBinder Abstract Class
    6.4.1 Fallback Methods
    6.4.2 Placing Canonical Binders on CallSites
    6.4.3 COM Support
    6.4.4 Class Summary
    6.4.5 Bind Method
    6.4.6 Defer Method
    6.4.7 ReturnType Property
    6.4.8 GetUpdateExpression Method
  6.5 GetMemberBinder Abstract Class
    6.5.1 Class Summary
    6.5.2 Name Property
    6.5.3 IgnoreCase Property
  6.6 SetMemberBinder Abstract Class
    6.6.1 Class Summary
    6.6.2 Name Property
    6.6.3 IgnoreCase Property
  6.7 DeleteMemberBinder Abstract Class
    6.7.1 Class Summary
    6.7.2 Name Property
    6.7.3 IgnoreCase Property
  6.8 GetIndexBinder Abstract Class
    6.8.1 Class Summary
    6.8.2 Calllnfo Property
  6.9 SetIndexBinder Abstract Class
    6.9.1 Class Summary
    6.9.2 Calllnfo Property
  6.10 DeleteIndexBinder Abstract Class
    6.10.1 Class Summary
    6.10.2 Calllnfo Property
  6.11 InvokeBinder Abstract Class
    6.11.1 Class Summary
    6.11.2 Calllnfo Property
  6.12 InvokeMemberBinder Abstract Class
    6.12.1 Class Summary
    6.12.2 Name Property
    6.12.3 IgnoreCase Property
    6.12.4 Calllnfo Property
    6.12.5 FallbackInvoke Method
  6.13 CreateInstanceBinder Abstract Class
    6.13.1 Class Summary
    6.13.2 Calllnfo Property
  6.14 ConvertBinder Abstract Class
    6.14.1 Class Summary
    6.14.2 Type Property
    6.14.3 Explicit Property
  6.15 UnaryOperationBinder Abstract Class
    6.15.1 Class Summary
    6.15.2 Operation Property
  6.16 BinaryOperationBinder Abstract Class
    6.16.1 Class Summary
    6.16.2 Operation Property
  6.17 CallInfo Class
    6.17.1 Class Summary
    6.17.2 ArgumentCount Property
    6.17.3 ArgumentNames Property
  6.18 BindingRestrictions Class
    6.18.1 Class Summary
    6.18.2 GetTypeRestriction Method
    6.18.3 GetInstanceRestriction Method
    6.18.4 GetExpressionRestriction Method
    6.18.5 Merge Method
    6.18.6 Combine Static Method
  6.19 CallSite and CallSite<T> Classes
    6.19.1 Class Summary
    6.19.2 Create Static Method
    6.19.3 Target Field
    6.19.4 Update Property
  6.20 StrongBox Class
    6.20.1 Class Summary
  6.21 DynamicObject Class
    6.21.1 Class Summary
  6.22 ExpandoObject Class
    6.22.1 Class Summary


Other documents:

Dynamic Language Runtime
DLR Hostirng Spec
Expression Trees v2 Spec
Getting Started with the DLR as a Library Author
SymPL Implementation on the Dynamic Language Runtime

Clone this wiki locally