VBScript Default Property Semantics
Here’s a question I recently got about VBScript, where by "recently" I mean August 28th, 2003. This code works just fine:
Set myObject = CreateObject("myObject")
myObject.myName = "Eric"
WScript.Echo myObject ' myName is the default property, so prints "Eric"
But myObject = "Robert" doesn't set the default property, it sets the variable to the string.
Why does reading work but writing fail? This works in VB6, why not VBScript?
In a strongly typed language such as VB6 the compiler can look at the compile-time types of the left and right sides of an assignment and determine that the type of the left hand side is an object, the right hand side is a string, and the object has a default property which is a string. The VB6 compiler can then generate the appropriate code to assign the string to the default property.
But what if you wrote a VB6 program using only variants? When the compiler sees
foo = bar typed as variants it cannot tell whether this means "set the default property of foo to bar" or "set foo to bar". The VB compiler chooses the latter every time. VBScript is a weakly typed subset of VB -- in VBScript, everything is a variant. So in VBScript, all assignments are treated as though the value is actually being assigned to the variable, not the default property. Therefore this is by design - this is for compatibility with VB6's behaviour. Had we written the same program in weakly-typed VB6, we'd get the same result.
I hear you exclaiming "The fact that a difference in available type information leads to a difference in run-time behaviour violates a basic principle of programming language design! Namely, the principle that late-bound calls have exactly the same semantics as early-bound calls!"
Indeed, as I've mentioned before, VB6 and VBScript violate this principle in several places. Default properties were, in my opinion, a bad idea all around, for this and other reasons. They make the language harder to parse and hence harder to understand. In particular, parameterless default properties make very little sense in VBScript. However, we are stuck with them now.
However, I should call out that there are some things we can do at runtime. Suppose
blah is an object with a property fred that is an object, and fred has a default property which is a string. If you say foo = blah.fred then foo is assigned the default value of fred even though we lack the compile-time type information. foo isn't set to the object fred. Of course, in this case we know to fetch the default property at runtime because we know the runtime type of fred and also know that there is no Set keyword. These two facts are sufficient to cause the runtime engine to always fetch the default property.
Or suppose you have an object
Baz with a property Table, Table has a default parameterized property Item which returns an object that has a string property Blah: Then this works just fine:
x = Baz.Table(1).Blah
But why? In this case we do not have enough compile-time information to determine that we really should call
Baz.Table.Item(1).Blah. The script engine has every reason to believe that Table is a function or parameterized property of Baz. This problem is solved by pushing it off to the implementation! The rule for implementers of IDispatch::Invoke is if all of the following are true:
- the caller invokes a property
- the caller passes an argument list
- the property does not actually take an argument list
- that property returns an object
- that object has a default property
- that default property takes an argument list
then invoke the default property with the argument list. Strange but true.
Perhaps unsurprisingly, not very many people know about that rule! This is yet another reason to never, ever write your own implementation of
IDispatch::Invoke. It also leads to some mind-numbingly complex code in the VBScript IntelliSense engine that Visual Studio uses. Making default property IntelliSense work with such a dynamically typed language was a piece of work, lemme tell ya. As I mentioned earlier, there are in fact some odd corner cases that are slightly different between VB6 and VBScript IntelliSense. I also talked a bit about left-hand-side default property semantics earlier.
Comments
Anonymous
August 30, 2005
"Default properties were, in my opinion, a bad idea all around, for this and other reasons. They make the language harder to parse and hence harder to understand."
A-men, brother.
Something that just struck me...in at least two places (default properties and With statements) VB/VBScript uses nothing at all in the source code to represent some important thing. This is strange because in most other places VB is liberally verbose compared to other languages.
There is dangerous potential for ambiguity when interpreting the meaning of nothing. Like when I ask my wife, "What's wrong?"Anonymous
August 30, 2005
The comment has been removedAnonymous
August 31, 2005
The comment has been removedAnonymous
August 31, 2005
Yeah, I meant C++, you are correct.Anonymous
September 07, 2005
I used to think that default properties were stupid because they necessitate a distinction between 'set' and 'let'. However, I now miss being able to say 'value = SomeControl' instead of C#'s 'if (SomeControl is CheckBox) value = (SomeControl as CheckBox).Checked.toString() else value = (SomeControl as TextBox).Text'.Anonymous
January 25, 2008
I found this site very useful.Anonymous
January 25, 2008
we can learn a lot about VBScript.Anonymous
October 07, 2009
The comment has been removedAnonymous
July 11, 2010
Thanks for this information - it has really helped me understand some issues. I am trying to upgrade a VB6 app to dot net - and the new app gets called from VBScript - so the behaviour between VB6 and .NET must match - but this will not work and it relates to issues that you have detailed above. I have created a class in dot net with a default property based on iCollection interface - this works fine - I can enumarete fine and referance it like NewCollection(1).TestProp - without issues - but when this collection is returned as a property from another object it does not work. - ie. ApplicationClass.NewCollection(1).TestProp does not work - but ApplicationClass.NewCollection.Item(1).TestProp does work - and so does "Set its = ApplicationClass.NewCollection" and than its(1).TestProp Also noticed that ApplicationClass.NewCollection()(1).TestProp does work. Is there anyway of getting .NET to behave the same way as what VB6 does - are there some attributes that I can apply to correct this behaviour??