我只是偶然发现了一个问题,我花了几个小时才找到原因。
我声明了一个类变量,ReadOnly因为我想在类的构造函数中设置它的值(以下所有代码都是示例代码,所以它可能没有多大意义)。
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
Run Code Online (Sandbox Code Playgroud)
经过一些编码后,我发现我想根据这个变量的值做其他事情,所以我将代码更改为:
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
Run Code Online (Sandbox Code Playgroud)
但是这个更改后的代码不起作用,变量_content从未获得我在构造函数中传递的值。Visual Studio 2019 没有显示错误,代码编译并运行。
经过大量的反复试验,我发现,我忘记删除ReadOnly变量声明中的 ,这就是我的问题的原因。
但是,当我尝试ReadOnly在构造函数之外设置变量时,为什么 Visual Studio 不警告我这种情况,或者为什么程序不会崩溃?ReadOnly变量声明中的这有什么用,因为它只会引起混乱且难以发现错误?
甚至在roslyn和vblang的 github 上也有人问过这个问题。有一个明确的答案:
Visual Basic 语言规范的第 130 页指出:Copy-in copy-back。如果传递给引用参数的变量类型与引用参数的类型不兼容,或者如果将非变量(例如属性)作为参数传递给引用参数,或者调用是后期绑定的,然后分配一个临时变量并将其传递给引用参数。传入的值将在调用方法之前复制到此临时变量中,并在方法返回时复制回原始变量(如果有且可写)。
甚至还有一个历史理由:
这是设计使然。在 VB6 中 ByRef 是默认值,因此传递任何参数都是 ByRef,包括 ReadOnly 属性。
如果您好奇,我已经创建了一个SharpLab 示例,其中包含您的代码的简化版本(我已经删除了与您的问题无关的任何内容,并放置了另一个非ReadOnly字段进行比较),您可以在其中观察正在发生的事情。生成的 C# 代码等同于生成的 IL 代码(您可以尝试将结果语言组合框更改为 IL,但除非您涉足 IL 代码编程,否则它更不可读)。