|In search of default properties|
|Written by Mike James|
|Wednesday, 13 April 2011|
Page 1 of 2
Default properties allow you to use direct assignment with an object. They are partially supported in .NET and C# in particular but can we do better? A look at indexers, operator overloading and implicit operators as ways of implementing default properties.
The idea of an object having a default property is a strange one. You can argue that it has no place in an object-oriented design because the act of assigning to an object doesn't make much sense. However default properties allow objects to participate in the rich micro-language that is the expression. For example, if an object has a default numeric property then you can write:
or similar in place of
Yes, it is syntactic sugar but what is wrong with a language being as sweet as possible?
There is also the possibility that the property can be a full get/set style property or even a property backed by a method call. For example suppose Rnd is an object that supplies random numbers then you could write:
or similar. The only objection to using objects in this way is that they look like functions - but what is wrong with objects that are functions? Other languages make a virtue out of functions being first class objects.
Of course the big problem with default values is how do you distinguish between:
which is a reference type assignment and
which is a default property assignment - assuming MyValue is of the same type as the default property?
The only reasonable answer is to implement a rule which uses reference type assignment when the assigned quantity is compatible with the class, default property assignment when it is compatible with the default property and throw an exception in all other cases. Even simpler use the rule that default property assignment only works when a default property is declared and only for value types. There are lots of ways to make it work.
The stack expression
This all started as part of a demonstration project I was working on. The idea was to create a stack class that could participate in an expression. That is
is equivalent to:
For the sack of a simple demonstration the stack class was limited to working with integers.
The basic stack class with just a push and a pop method are:
public class stack
Nice and simple and storing and retrieving a value is just a matter of:
stack MyStack=new stack();
Of course you could have implemented the same operations not as methods but as a property:
public int value
Using the stack via apropery is just
MyStack.value = 20;
And in case you are worried about a property called value and the value keyword then it is worth pointing out that one is this.value and the other just value.
Using get/set properties to implement the stack pop and push is about as good as it gets in C# and as long as you are prepared to write the property name within the expression everything works. Of course some programmers might criticize the unnatural operation of a property that doesn't obey the standard semantics of assignment but we are trying to be adventurous here....
Once upon a time there was a default property in Visual Basic but it was removed to harmonise with C# and the CLR. This seems a bit crazy as it would have been more useful to add a true default property to C# than remove a useful facility.
What we do have in both C# and VB is the indexer - so close to a default property and yet so far...
An indexer allows you to treat an object as it if it had a default property that is an array. That is you can write things like
Notice that value can be of any compatible type but there is no way you can drop the [i].
To create an indexer you simply define a property called "this"
public returntype this[indextype index]
This passes an index of indextype to the indexer which then behaves as if it was a property of type returntype.
So to add an indexer to our stack class we would write something like:
public int this[int index]
following this we can now use the stack via commands like:
MyStack = 30;
Notice that in this case the index isn't actually used. You could pass in any value and the result would be the same. There are applications of the indexer where the value of the index controls what happens as in an array or collection access and this is its more normal use. Notice that you can use more complex types as the index and not just an integer and this opens up some possibilities.
However as far as default properties go this is about as good as it gets.
|Last Updated ( Monday, 12 July 2021 )|