C# "dynamic," Part II


Yesterday, I made an attempt to introduce the C# dynamic feature by describing what change there has been to the language, what scenarios we hope to affect by that change, and some overview of what dynamic operations look like in C# 4 as it stands in the preview distributed at PDC 2008.


One thing that I did not mention was this: that the language changes are entirely focused around consumption of dynamic types, not definition of those types. This is not to say that you cannot define dynamic types–just that the C# language has not change in any way so as to provide you a shortcut. We have not added a “method_missing,” nor are any of the regular types you define in C# capable of somehow dynamically acquiring properties. And you can’t say something like “dynamic class C” to define such a class.


If you want to define a new type that accepts dynamic operations, then it’s easy to do so, and you don’t even need C# 4. You just implement IDynamicObject IDynamicMetaObjectProvider, which is the interface that tells the DLR, “I know how to dispatch operations on myself.” It’s a simple interface, in the spirit of IQueryable, with a single method that returns a “MetaObject” “DynamicMetaObject” to do the real heavy lifting.


I want to make clear at this point that I am about to provide an example of how to use the DLR, but that I am only doing so in order to demonstrate how these objects play with the C# 4 language features. There are other blogs that are certainly better sources for information about the DLR.


So, here’s an implementation:

public class MyDynamicObject : IDynamicMetaObjectProvider
{
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new MyMetaObject(parameter, this);
}
}

Simple enough! Here’s the MyMetaObject definition. The MetaObject DynamicMetaObject “knows how” to respond to a variety of actions including method calls, property sets/gets, etc. I’ll just handle those (no best practices here; this is a minimal implementation):

public class MyMetaObject : DynamicMetaObject
{
public MyMetaObject(Expression parameter, object value)
: base(parameter, BindingRestrictions.Empty, value)
{
}

public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return this.PrintAndReturnIdentity(“InvokeMember of method {0}”, binder.Name);
}

public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
return this.PrintAndReturnIdentity(“SetMember of property {0}”, binder.Name);
}

public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
return this.PrintAndReturnIdentity(“GetMember of property {0}”, binder.Name);
}

private DynamicMetaObject PrintAndReturnIdentity(string message, string name)
{
Console.WriteLine(String.Format(message, name));
return new DynamicMetaObject(
Expression,
BindingRestrictions.GetTypeRestriction(
Expression,
typeof(MyDynamicObject)));
}
}


This is exactly what, say, IronPython might do to implement the dictionary lookup that it needs to do for its members. And when I say “exactly,” I mean “sort of” because of course IronPython’s implementation is considerably more complex and robust. But this gets the job done. Suppose I want to now use C# to invoke these things using the new syntax. That’s the easy part! Check this out:

public class Program
{
static void Main(string[] args)
{
dynamic d = new MyDynamicObject();

d.P3 = d.M1(d.P1, d.M2(d.P2));
}
}


So I take my MyDynamicObject that I defined above, and then I get a few properties, call a few methods, and set a property for good measure. If you compile this, you get the following output:


GetMember of property P1
GetMember of property P2
Call of method M2
Call of method M1
SetMember of property P3

I think that’s pretty cool.


Next time I’ll talk more about the dynamic type again, in the C# language, and how it behaves. I just wanted to take a little diversion to clear up the fact that we’ve really done a lot of cool work on the consumption side, and your code ought to look better for it when you’re consuming these things. If you’re defining them, then you’re in DLR-land, and you might even be writing in python or ruby or some other language. I’ll provide pointers to those issues as I get them.


Previous posts in this series: C# “dynamic”

Comments (21)

  1. Hey, Chris — do you think you could explain the parameter argument to GetMetaObject, and how/why it’s supposed to be used?

  2. cburrows says:

    Hi Keith,

    The DLR uses expression trees internally to communicate rules and actions that are the result of binding particular operations. The parameter parameter is a ParameterExpression that represents the target object.

    I would prefer to spend my time talking about C#, so I’m not going to pursue the details of the DLR implementation much deeper than that. Unfortunately I also don’t have any PDC-era pointers for you, although Martin Maly’s blog is a great resource (http://blogs.msdn.com/mmaly/). You should install the CTP and debug around. The code I’ve posted compiles and works with it.

    chris

  3. kfarmer says:

    Unfortunately, Martin’s blog is absent this info.  I’ll see if I can prod him or his cohorts about it if I remember.  It’s been a while since I was active in DLR-land (ie, shortly after I joined MSFT).

    Back to C# itself, part of why I ask, actually, is in response to the known limitation dynamic method binding in C# not being able to bind extension methods.  I’m pondering what sort of workarounds would be possible, if any.

    For that matter, I wonder why it’s even a limitation?  The compiler could embed information about what extension methods would have been in-scope at that location during the original compile, and pass that to the late-binding algorithm.  Or is this the DLR’s limitation?  (I remember looking into it a while ago, and almost had it hacked into submission)

    Or is it just below the feature bar, like yield foreach? 🙂

  4. Here are a few good resources that you ought to look at for information about dynamic from PDC: Anders

  5. RednaxelaFX says:

    @Keith

    My guess that the reason of not being able to bind extension methods is because this piece of info of whether an extension method is in scope of not is not available to the binders at runtime. The default binder for CLR objects can only find out what members a type has, and extension methods are not a part of the type to be extended; there isn’t quite a way to pass this piece of info into the DLR in the way it is designed now.

    Well, someone might be able to get a smarter binder to do this, of course. If a binder’s constructor can take a list of possibly applicable extension methods’ MethodInfo (which the C# compiler would know at compile time), then it’s possible for the binder to get extension methods working.

  6. @RednaxelaFX

    Your second paragraph was the point I was trying to make.  Having spelunked through earlier releases about a year ago, I know roughly where the DLR would need that information.  It’s just a matter of putting it there and, of course, testing it to within an inch of someone else’s life.

    That, unfortunately, I think would be the biggest blocker in having this capability in C#4.

  7. RednaxelaFX says:

    @Keith

    Well, I’d like to put it this way:

    Think about it, in IronPython, Python types that map onto plain CLR types can have extensions over them; and the same is with IronRuby’s Ruby types and all other languages based on the DLR, except…C# and VB I guess. The reason for this is that C# and DLR make extension types in a different way. C# extension methods are just syntactic sugar resolved at compile time and doesn’t really carry into runtime, where as IronPython extension methods for Python types register themselves to the binder with attributes. Thus C# extension methods are only available in a certain scope at compile time (which is somehow a good thing, less confusion), while Python extensions affect the whole Python part of the program.

    If one wants the same semantics as what IronPython extensions give, he/she can simply implement extension methods simillar to the way IronPython does today — with the restriction that the target type to be extended has to implement IDynamicObject for custom dynamic lookups, which, probably turns into inheriting from System.Dynamic.DynamicObject, which might not be feasible.

    The other way around, the C# compiler will have to generate a list of possibly applicable extension methods in scope, and pass that list into the call payload at the call site, so that C# binder has a chance of picking it up later at runtime. Haven’t thought about the implications of this; it might imply further complicated scoping rules, and that’d be a problem.

    @Chris

    I’d like to ask a question: where’s that System.Dynamic.DynamicObject class in Anders’ and Jim’s talk? Didn’t find it in System.Core.dll in the CTP. Tried the IDO impl sample from C# Future and it worked, though.

    • RednaxelaFX (Kris Mok)
  8. cburrows says:

    Re: the lack of support for extension methods. You’re both on the right track. Extension methods are a compile-time feature that would have required us to push some context into the C# Runtime Binder. It wouldn’t have been impossible, but consider that extension methods are often used in LINQ scenarios and that there, we have a bigger problem of converting lambdas to "dynamic." So we decided to punt on this for C# 4. All decisions like this are difficult and complicated.

  9. cburrows says:

    RednaxelaFX, DynamicObject is not in the CTP. I mentioned that in Part III. Sorry!

  10. Tobi says:

    Hey RednaxelaFX – if you want to look at a more detailed example on how to implement IDynamicObject take a look at my post http://saftsack.fs.uni-bayreuth.de/~dun3/archives/first-look-ducktyping-c-4-0-idynamicobject-metaobject/202.html – I don’t go into details, but it might help you figure the parameters out.

    Cheers,

    Tobi

  11. RednaxelaFX says:

    @Tobi

    Thanks for the link. 🙂

    I didn’t have any problems with understanding IDynamicObject and implementing my own, though…in fact I implement it a few times already. I implemented my first one back in May for my bachelor DP. Later on, the whole IDynamicObject interface changed and got deprecated as the IOldDynamicObject, and a newer version of IDynamicObject appeared, with a clearer MetaObject Protocol.

    I’m writing a converter in a different perspective from yours, and I’ll see if I can clean the code up a little so that I can write a post about it. Thanks again for the link, didn’t think of using the Castle library before.

    • RednaxelaFX
  12. Welcome to the 47th Community Convergence. We had a very successful trip to PDC this year. In this post

  13. Tobi says:

    @RednaxelaFX

    Sorry, totally phased out and copied the wrong name. 😀 I guess I was thinking about Keith J. Farmer. Sorry. 😀

    Anyway, I am looking forward to your post about the converter. I would be glad, if you could drop me a link!

    Cheers,

    Tobi

  14. Today, let’s geek out about the language design regarding the dynamic type. Type in the language vs.

  15. I’m messing around with the VS 2010 CTP but I can’t find the System.Dynamic namespace.  What assembly is this in?

  16. RednaxelaFX says:

    @KeithH,

    It’s not there in the CTP. But you may want to check out the lastest version of DLR, which has this System.Dynamic namespace. You can find it in the source drops on IronPython’s CodePlex site.

  17. Let’s look at this: dynamic d = null ; object o = d; // not an implicit conversion Last time , I said

  18. ignu says:

    I’m having a difficult time grocking MetaObject Call, and getting it to actually return an object.

    http://stackoverflow.com/questions/283143/can-i-implement-methodmissing-in-c-4-and-have-it-actually-return-a-value

  19. We left off last time with this piece of code public class C { public static void M( int i) { } public

  20. Very good resources for the coming version… Sam Ng Dynamic in C# Part One Dynamic in C# Part Two Chris

  21. It has been a while since I posted anything here, and I have the same excuse everyone else does. Work,