Freigeben über


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 readonly

  • Anonymous
    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 removed

  • Anonymous
    June 16, 2010
    The comment has been removed

  • Anonymous
    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.