다음을 통해 공유


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

  • Anonymous
    October 28, 2008
    Hey, Chris -- do you think you could explain the parameter argument to GetMetaObject, and how/why it's supposed to be used?
  • Anonymous
    October 28, 2008
    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
  • Anonymous
    October 28, 2008
    The comment has been removed
  • Anonymous
    October 29, 2008
    Here are a few good resources that you ought to look at for information about dynamic from PDC: Anders
  • Anonymous
    October 29, 2008
    @KeithMy 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.
  • Anonymous
    October 29, 2008
    @RednaxelaFXYour 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.
  • Anonymous
    October 29, 2008
    @KeithWell, 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.@ChrisI'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)
  • Anonymous
    October 30, 2008
    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.
  • Anonymous
    October 30, 2008
    RednaxelaFX, DynamicObject is not in the CTP. I mentioned that in Part III. Sorry!
  • Anonymous
    November 03, 2008
    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
  • Anonymous
    November 03, 2008
    The comment has been removed
  • Anonymous
    November 03, 2008
    Welcome to the 47th Community Convergence. We had a very successful trip to PDC this year. In this post
  • Anonymous
    November 03, 2008
    @RednaxelaFXSorry, totally phased out and copied the wrong name. :-D I guess I was thinking about Keith J. Farmer. Sorry. :-DAnyway, I am looking forward to your post about the converter. I would be glad, if you could drop me a link!Cheers,Tobi
  • Anonymous
    November 06, 2008
    Today, let's geek out about the language design regarding the dynamic type. Type in the language vs.
  • Anonymous
    November 10, 2008
    I'm messing around with the VS 2010 CTP but I can't find the System.Dynamic namespace.  What assembly is this in?
  • Anonymous
    November 10, 2008
    @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.
  • Anonymous
    November 10, 2008
    Let's look at this: dynamic d = null ; object o = d; // not an implicit conversion Last time , I said
  • Anonymous
    November 12, 2008
    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
  • Anonymous
    November 14, 2008
    We left off last time with this piece of code public class C { public static void M( int i) { } public
  • Anonymous
    December 15, 2008
    Very good resources for the coming version... Sam Ng Dynamic in C# Part One Dynamic in C# Part Two Chris
  • Anonymous
    February 03, 2009
    It has been a while since I posted anything here, and I have the same excuse everyone else does. Work,