我正在开发一个用 VB.NET 编写的项目。该项目有几个结构,它们曾经具有可写字段。我用只读属性替换了所有这些字段,并编写了函数来创建其属性之一已更改的结构的副本。
我假设尝试写入这些属性之一的代码的每一部分都会成为错误,然后我可以通过使代码调用新函数来简单地修复所有错误。令我沮丧的是,如果不小心将 ReadOnly 属性传递给函数的 ByRef 参数,编译器会在没有警告的情况下接受此属性,并且分配的值将被默默丢弃!
这是一个例子:
Structure Point
Public ReadOnly Property X As Integer
Public ReadOnly Property Y As Integer
End Structure
Module Module1
Sub IncreaseByOne(ByRef x As Integer)
x = x + 1
End Sub
Sub Main()
Dim point As New Point
IncreaseByOne(point.X)
Console.WriteLine($"point.X is {point.X}")
End Sub
End Module
我希望 IncreaseByOne(point.X)
行会抛出一个错误,或者至少是一个警告,因为 point.X
是只读的,通过引用传递它没有意义。相反,代码编译时不会出现任何警告,并且值会分配给 x
IncreaseByOne
里面被默默丢弃,程序打印 point.X is 0
.
如何检测代码中将只读属性传递到通过引用获取该属性的函数中的所有位置?我能想到的唯一方法是遍历我拥有的每个只读属性,找到该属性用作参数的所有位置,并查看该参数是否为 ByRef。这将非常耗时,但如果没有其他解决方案,那就是我会做的。
我正在使用 Visual Studio 2019。我愿意安装新软件来实现此目的。
最佳答案
这真的很有趣。 VB.NET 编译器确实试图使属性看起来像变量。即使我明确将该属性声明为
Structure Point
Dim _x As Integer
ReadOnly Property X() As Integer
Get
Return _x
End Get
End Property
End Structure
代码像以前一样编译和执行。如果添加了属性 setter ,它甚至可以正常工作!
Structure Point
Dim _x As Integer
Property X() As Integer
Get
Return _x
End Get
Set(value As Integer)
_x = value
End Set
End Property
End Structure
经过上述更改,程序正确打印 1。
查看生成的 IL,我们可以看出原因:
IL_0009: ldloca.s point
IL_000b: call instance int32 VisualBasicConsoleTest.Point::get_X()
IL_0010: stloc.1 // Store returned value in local variable
IL_0011: ldloca.s // load address of that local variable (and pass to function call)
IL_0013: call void VisualBasicConsoleTest.Program::IncreaseByOne(int32&)
IL_0018: nop
IL_0019: ldloca.s point
IL_001b: ldloc.1 // Load contents of local variable again
IL_001c: call instance void VisualBasicConsoleTest.Point::set_X(int32) // and call setter
尽管我们预计会出现错误,因为属性不是值(并且 byref 需要值),但编译器会伪造我们可能想要的内容:他实际上生成了对 getter 的调用,将值存储在堆栈上,将对堆栈的引用(!)传递给被调用的函数,然后使用该值调用 setter 。
这适用于这个简单的场景,但我同意上面的评论者的观点,当详细查看它时,这可能会非常令人困惑。如果属性实际上是计算属性,则结果是任意的(尝试将 getter 实现为 Return _x + 1
...)
C# 会在这里抛出错误,因为属性不是值,因此不能用作 out 或 ref 参数。
关于vb.net - 如何检测何时意外通过引用传递了 ReadOnly 属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74709423/