Core9: readonly auto-properties
[This post is part of a series, "wish-list for future versions of VB"]
IDEA: Readonly auto-properties. We should allow read-only properties, which are backed by readonly fields, and can be set by either a property-initializer or through the existing "New ... With" syntax.
SCENARIO: You want your class to expose a collection, so that users can add or remove elements. But you don't want them to actually change the list object itself:
Property p As IList(Of Integer) = New List(Of Integer) From {1, 2, 3}
SCENARIO: You want to create a simple immutable record. Currently the language fights against you every step of the way. It could be as easy as this:
Class Point
ReadOnly Property x As Integer = From New
ReadOnly Property y As Integer = From New
End Class
Dim t As New Point With {.x = 15, .y = 27}
The first scenario would be translated into this by the compiler:
ReadOnly Property p As IList(Of Integer)
Get
Return _p
End Get
End Property
Private ReadOnly _p As IList(Of Integer)
Sub New()
_p = New List(Of Integer) From {1, 2, 3}
End Sub
The initialization code "_p = ..." would be injected into every constructor, as is done with the current auto-properties.
As for the second scenario, it is more awkward to implement. We could translate it into the following. It would be an error if the user had already provided a constructor.
Class Point
ReadOnly Property x As Integer
Get
Return _x
End Get
End Property
ReadOnly Property y As Integer
Get
Return _y
End Get
End Property
Private ReadOnly _x As Integer
Private ReadOnly _y As Integer
Sub New(ByVal x As Integer, ByVal y As Integer)
_x = x
_y = y
End Sub
End Class
Dim t As New Point2(x:=15, y:=27) With {}
The "New ... With" translation is interesting. What we could say is this: "With {.x=15, .y=27}" will set mutable properties "x" and "y" if they exist (for backwards compatibility). If they don't exist but there do exist readonly auto-properties of the same name, then they will be moved as named arguments into the constructor.
There is a lot of magic going on in translating the second scenario. It might be too much. We would also have to be careful that the user remains able to set attributes conveniently enough, e.g. for serialization.
The first scenario is also not quite answered properly. Usually when you expose an IEnumerable(Of T) you really intend wrap your own private mutable list as a ReadOnlyCollection(Of T), so as to prevent sneaky users from casting the IEnumerable(Of T) into an IList(Of T) and then modifying your list.
Users have also asked for a way to have private setters for auto-properties. It might be that this is a more useful scenario rather than readonly.
Provisional evaluation from VB team: This is a decent idea, one that we should consider against the other decent ideas. Readonly datastructures and immutable datastructures are a Good Thing. And we should invent syntax for them that's as easy or easier than the equivalent syntax for mutable data, so that users can "stumble into a pit of virtue".
Comments
Anonymous
January 30, 2010
I'd like having a ReadOnly auto-property that can be set in the class (without creating a corresponding field) but not set outside the class. Scenerio: you can set readonly property in constructor (or elsewhere in the class) Class Point Sub New(x As Integer) Me.X = x End Sub ReadOnly Property X As Integer End Class Scenerio: but you can't set outside the class Dim point = New Point(1) point.X = 2 'this won't compile b/c readonlyAnonymous
February 01, 2010
I'd expect readonly properties to work the same way as readonly fields. Indeed, for collections it makes sense to have them in readonly properties, but it requires too much syntax. So the first example (Readonly Property p As IList(Of Integer) etc.) makes sense to me. The Point example doesn't make sense. I'd expect to have to set a readonly property in the declaration or the constructor just like I do with readonly fields. So: Class Point ..ReadOnly Property x As Integer ..ReadOnly Property y As Integer ..Public Sub New(x As Integer, y As Integer) ....Me.x = x ....Me.y = y ..End Sub End Class The advantage would be the compact Property lines. I wouldn't expect readonly properties to work with initializers; if you're making the properties readonly, then I figure you should have to provide an appropriate constructor. I can see where this would cause problems (e.g. cases where a parameterless constructor is required), but I'm not sure the convoluted syntax in the last example is a good solution.Anonymous
February 13, 2010
The comment has been removedAnonymous
June 16, 2010
The comment has been removedAnonymous
March 04, 2012
This is an interesting issue. You do need a way to specify mixed access levels in a property, consider a property that has a private setter but a protected getter, I would say it should be declared Public ReadOnly Property MyProperty As String generated: Private _MyProperty As String Public ReadOnly Property MyProperty As String Get Return _MyProperty End Get Private Set(value As String) _MyProperty = value End Set End Property And vice versa.