vb.net - 如何检测何时意外通过引用传递了 ReadOnly 属性?

标签 vb.net visual-studio

我正在开发一个用 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/

相关文章:

vb.net - 泛型类型上的 GetType

vb.net - 如何添加一个项目作为另一个项目的引用

VB.NET - 实现 IDisposable 时是否应该添加 Finalize 方法?

c# - 批量更新字典内多个记录的属性 (VB.NET/C#)

c# - 将 PDF 嵌入到 WPF 应用程序中

c++ - visual studio 2012 c++ hello world - iostream 不工作

visual-studio-2010 - 如何将大型 VS 解决方案分解为更小的解决方案

.net - 标签: Any reason I should?的生成成员

visual-studio - 我的 Visual Studio 程序是 "Copyright © Hewlett-Packard Company"

c# - 我可以使用 Visual Studio C# 类库项目引用 Blazor 项目