Ayende @ Rahien

Refunds available at head office

Dynamic resolution rules joy

In the following code, what do you believe the output should be?

class Program
{
    static void Main(string[] args)
    {
        dynamic stupid = new Stupid{Age = 3};

        Console.WriteLine(stupid.Age);
    }
}

public class Stupid : DynamicObject
{
    public int Age { get; set; }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = 1;
        return true;
    }
}

Argh!

The default is to use the actual type members, and then fall back to the dynamic behavior. Whereas I would expect it to first check the dynamic behavior and then fall back to the actual members if it can’t find dynamic stuff.

Quite annoying.

Tags:

Posted By: Ayende Rahien

Published at

Originally posted at

Comments

tobi
10/21/2011 10:30 AM by
tobi

Never worked with the DLR but I believe that DynamicObject is not the lowest level of abstraction. Maybe you can overrride this stupid behavior by overriding DynamicMetaObject GetMetaObject and returning a more well-behaved object.

sw
10/21/2011 10:46 AM by
sw

Why should it go the slower route of dynamic/reflection when there is a simple fast property on the object?

tobi
10/21/2011 10:53 AM by
tobi

Because the whole point of dynamic is to get in control of the members that your object demonstrates to have.

Mix
10/21/2011 10:55 AM by
Mix

So what did you actually expect it to show? 3 is printed as I would've expected it.

Matt
10/21/2011 11:05 AM by
Matt

Seems perfectly reasonable to me. I'd consider it stupid if it had printed 1.

Mix
10/21/2011 11:05 AM by
Mix

MSDN article on TryGetMember is completely misleading - had I read it before posting my comment I would've expected 1 indeed.

Bogdan Marian
10/21/2011 11:08 AM by
Bogdan Marian

@Ayende I find this behaviour normal. The notion of TryXXX (found when parsing numbers or getting stuff out of dictionaries) is related to uncertainty. Thus, if the CLR sees a getter, it should call that getter and not call the TryGetMember method.

Khalid Abuhakmeh
10/21/2011 11:09 AM by
Khalid Abuhakmeh

I find that quite annoying as well, that if I want to use a dynamic object's property I have to cast it before passing it into any method. In my eyes that defeats the purpose of using a dynamic to begin with since it has that additional development overhead/noise.

Maybe they can fix it in the next .Net where it can inspect the parameter type of the method and just try to do a best guess auto-cast.

Frederico Pinto
10/21/2011 11:13 AM by
Frederico Pinto

This comes from the "Remarks" section of the MSDN article on TryGetMember:

"You can also add your own members to classes derived from the DynamicObject class. If your class defines properties and also overrides the TrySetMember method, the dynamic language runtime (DLR) first uses the language binder to look for a static definition of a property in the class. If there is no such property, the DLR calls the TrySetMember method."

3 it is... or am I missing something?

Bogdan Marian
10/21/2011 11:14 AM by
Bogdan Marian

Reading the MSDN link for DynamicObject.TryGetMember (http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.trygetmember.aspx), I found this inside the remarks block: "You can also add your own members to classes derived from the DynamicObject class. If your class defines properties and also overrides the TrySetMember method, the dynamic language runtime (DLR) first uses the language binder to look for a static definition of a property in the class. If there is no such property, the DLR calls the TrySetMember method.". So, the behaviour encountered inside the code above seems to be the correct one and is similar to the one described by the official documentation.

flukus
10/21/2011 11:49 AM by
flukus

I've never used dynamic object at all, nor read much about it.

Intuitively I would expect the getter t8o take precedence.

zdeslav
10/21/2011 12:00 PM by
zdeslav

seems completely logical to me. Imagine:

public class Stupid : DynamicObject { public int Age { get; set; } public int OtherProperty { get; set; } public int AndAnother { get; set; } ...

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    // if this would have precedence over the specified properties
    // here you would have to filter all of those properties
    // that would render explicit properties quite useless
    // if(binder.Name == "Age" ...
}

}

Lee Atkinson
10/21/2011 12:35 PM by
Lee Atkinson

I think the confusion lies in that people assume that the dynamic keyword and the DynamicObject type are somewhat related - they aren't (they just happen to be used together lots of times).

It makes sense to me that you get the 'statically-defined' member rather than the result of TryGetMember. It most closely resembles 'normal' inheritance - start with the object and work up the inheritance tree until you find member you are looking for.

(The dynamic keyword just kets you late-bind to the object and thus make it 'more readable' as a simple member.)

Bill Barry
10/21/2011 01:34 PM by
Bill Barry

@zdeslav:

public override bool TryGetMember(GetMemberBinder binder, out object result) { if(binder.StaticInvoke!=null) return binder.StaticInvoke(out result); }

Still I think the current design is better, but it wouldn't have to be as bad as you are assuming.

Frank Quednau
10/21/2011 01:59 PM by
Frank Quednau

Just proves that however logical an API appears to be, there is always someone who doesn't like it the way it is. I like that as a sort of comforting start of weekend thought.

Jamie
10/21/2011 02:44 PM by
Jamie

It seems like what you want is to use the dynamic behavior to essentially override a default value for some property: the only time you want to get the actual property value would be if nothing had been assigned. So the "setter" seems quite pointless if you expect the dynamic behavior to take precedence.

So why not just create a default dynamic value instead of a property? E.g. in your constructor just call TrySetMember to add some default for a dynamic value, instead of hardcoding a property that you want to allow to be overridden.

I think this makes sense, but if you don't like it, then just implement your own IDynamicMetaObjectProvider.

Michael L Perry
10/21/2011 04:21 PM by
Michael L Perry

This is standard behavior for any dynamic language. Smalltalk's doesNotUnderstand is only called if the object is sent a message that it does not understand. Ruby's method_missing is only called when a method is truly missing. I think it would be more annoying if .NET were to choose the opposite default.

JC
10/21/2011 05:06 PM by
JC

Maybe the method should have been named TryGetMissingMember instead hence avoiding any confusion about it's functionality.

Jon
10/21/2011 07:54 PM by
Jon

Agree with Michael. This is the behavior I would expect.

Nick Parker
10/22/2011 05:43 PM by
Nick Parker

Seems fitting that it would infer the explicit assignment first, then fall through to an implicit when unavailable.

Omri
10/22/2011 06:10 PM by
Omri

I believe the confusion comes from prior experience with Dynamic Proxies. one should understand that dynamic proxies and dynamic object are different. the behavior you are expecting is very reasonable in a dynamic proxy scenario. it is less reasonable in a dynamic object.

Darius Damalakas
10/23/2011 08:06 AM by
Darius Damalakas

Standard behaviour, if I remember correctly, Python does exactly the same, and that makes perfect sense

Chris Marisic
10/24/2011 01:54 PM by
Chris Marisic

I think JC summed up this post, that the method is named very poorly.

That if the method was named TryGetMissingMember that the intent would be conveyed properly.

Stanislav Ageev
10/25/2011 03:57 PM by
Stanislav Ageev

Darius is right, getattr in Python got exactly the same behavior which makes sense. I was missing this behavior a lot

Comments have been closed on this topic.