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.
Comments
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.
Why should it go the slower route of dynamic/reflection when there is a simple fast property on the object?
Because the whole point of dynamic is to get in control of the members that your object demonstrates to have.
So what did you actually expect it to show? 3 is printed as I would've expected it.
Seems perfectly reasonable to me. I'd consider it stupid if it had printed 1.
MSDN article on TryGetMember is completely misleading - had I read it before posting my comment I would've expected 1 indeed.
@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.
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.
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?
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.
I've never used dynamic object at all, nor read much about it.
Intuitively I would expect the getter t8o take precedence.
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; } ...
}
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.)
@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.
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.
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.
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.
Maybe the method should have been named TryGetMissingMember instead hence avoiding any confusion about it's functionality.
Agree with Michael. This is the behavior I would expect.
Seems fitting that it would infer the explicit assignment first, then fall through to an implicit when unavailable.
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.
Standard behaviour, if I remember correctly, Python does exactly the same, and that makes perfect sense
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.
Darius is right, getattr in Python got exactly the same behavior which makes sense. I was missing this behavior a lot
Comment preview