Nostromo
Nostromo

Reputation: 1274

What is ReadOnly in a variable declaration good for?

I just stumbled upon a problem which took me hours to find the reason for.

I declared a class variable as ReadOnly because I wanted to set its value in the constructor of the class (all following code is example code, so it probably doesn't make much sense).

Private ReadOnly _content as String

Public Sub New(content As String)
    _content = content
End Sub

Public ReadOnly Property Content As String
    Get
        Return _content
    End Get
End Property

After a little bit of coding, I figured out that I wanted to do other stuff depending on the value of this variable, so I changed my code to:

Private ReadOnly _content as String

Public Sub New(content As String)
    Me.Content = content
End Sub

Public Property Content As String
    Get
        Return _content
    End Get
    Private Set(value As String)
        If Me.SetValue(Of String)(_content, value) AndAlso (_content = "foo") Then
            'other stuff
        End If
    End Set
End Property

Private Function SetValue(Of T)(ByRef storage As T, value As T) As Boolean
    storage = value
    Return True
End Sub

But this changed code didn't work, the variable _content never got the value I passed in the constructor. Visual Studio 2019 showed no error and the code compiled and ran.

After a lot of trial and error I found out, that I forgot to remove the ReadOnly in the variable declaration, and that was the reason for my problem.

But why doesn't Visual Studio warn me about this situation, or why doesn't the program crash, when I try to set a ReadOnly variable outside of the constructor? What is this ReadOnly in the variable declaration good for, when it can only cause confusion and hard to find errors?

Upvotes: 3

Views: 882

Answers (2)

xanatos
xanatos

Reputation: 111940

This question was asked even on the github of roslyn and of vblang. There was a definite answer:

Page 130 of the Visual Basic Language Specification states: Copy-in copy-back. If the type of the variable being passed to a reference parameter is not compatible with the reference parameter's type, or if a non-variable (e.g. a property) is passed as an argument to a reference parameter, or if the invocation is late-bound, then a temporary variable is allocated and passed to the reference parameter. The value being passed in will be copied into this temporary variable before the method is invoked and will be copied back to the original variable (if there is one and if it's writable) when the method returns.

There was even an historical justification:

This is by design. In VB6 ByRef was the default so passing any argument would have been ByRef, ReadOnly properties included.

If you are curious, I've created a SharpLab example with a simplified version of your code (I've removed anything that wasn't central to your problem and put another non-ReadOnly field for comparison) where you can observe what is happening. The C# code generated is equivalent to the IL code generated (you can try changing the Results language combo box to IL but it is much more unreadable unless you dabble in IL code programming).

Upvotes: 7

XLars
XLars

Reputation: 177

It ensures the field can only be set from the constructor or in it's declaration. If you for example have multiple constructors, they can set a different value for the read-only field, after that it cannot be modified anymore. This is different from using a constant value that can not change.

Upvotes: -1

Related Questions