为什么 Property Setter 接到的电话比预期的要多?

发布于 2024-12-09 05:46:38 字数 2602 浏览 3 评论 0原文

我在 VB.net 中观察到一种行为,即与姐妹 setter 方法的调用一起,属性 setter 的调用频率比看起来必要的要多。

Public Class Form1

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Console.WriteLine("Calling WorkReferenceTypeByReference")
        WorkReferenceTypeByReference(ReferenceTypeData)
        Console.WriteLine("Called WorkReferenceTypeByReference")
        Console.WriteLine("Calling WorkReferenceTypeByValue")
        WorkReferenceTypeByValue(ReferenceTypeData)
        Console.WriteLine("Called WorkReferenceTypeByValue")
    End Sub

    Public Sub WorkReferenceTypeByReference(ByRef ref As Point)
        Dim b As Point = New Point(4, 4) + ref
        Console.WriteLine("    adding (4,4) to " & ref.ToString)
    End Sub

    Public Sub WorkReferenceTypeByValue(ByVal ref As Point)
        Dim b As Point = New Point(4, 4) + ref
        Console.WriteLine("    adding (4,4) to " & ref.ToString)
    End Sub

    Private m_ReferenceType As Point = New Point(0, 0)
    Public Property ReferenceTypeData As Point
        Get
            Console.WriteLine("  Calling ReferenceTypeData getter")
            Console.WriteLine("  returning: " & m_ReferenceType.ToString)
            Return m_ReferenceType
        End Get
        Set(ByVal value As Point)
            Console.WriteLine("  Calling ReferenceTypeData setter")
            Console.WriteLine("  value = " & value.ToString)
            m_ReferenceType = value
        End Set
    End Property
End Class

前面的代码向控制台返回以下输出,

Calling WorkReferenceTypeByReference
  Calling ReferenceTypeData getter
  returning: {X=0,Y=0}
    adding (4,4) to {X=0,Y=0}
  Calling ReferenceTypeData setter
  value = {X=0,Y=0}
Called WorkReferenceTypeByReference
Calling WorkReferenceTypeByValue
  Calling ReferenceTypeData getter
  returning: {X=0,Y=0}
  adding (4,4) to {X=0,Y=0}
Called WorkReferenceTypeByValue

请注意方法执行后对属性设置器的虚假调用。我认为这种行为是作为一种安全措施而产生的,以防止无意中修改底层属性,尽管这可能是意图。

ByRef 与 ByVal 使用情况下的这种行为很容易通过选择适当的 ByVal 关键字来解决,但是最近注意到一种更阴险的行为,这种行为导致了重复调用的堆栈溢出,因为 setter 调用会更新调用仅吸气剂。

Public Sub DoSomething()
    Dim a As New CustomObject(anotherObject.AProperty(getterArgument))
End Sub

Public Class AnotherObject

    Public Property AProperty as SomeType
        Get
            ' Get value
        End Get
        Set
            ' Set value, call DoSomething
        End Set
    End Property
End Class

在前面的示例中,调用 DoSomething() 将触发 AProperty getter 方法,但在使用之后,将触发 setter 方法,该方法根据程序逻辑再次调用 DoSomething()。让我困惑的是二传手的自动调用。

I have observed a behaviour in VB.net where property setters get called more often than seems necessary, in conjunction with calls to the sister setter method.

Public Class Form1

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Console.WriteLine("Calling WorkReferenceTypeByReference")
        WorkReferenceTypeByReference(ReferenceTypeData)
        Console.WriteLine("Called WorkReferenceTypeByReference")
        Console.WriteLine("Calling WorkReferenceTypeByValue")
        WorkReferenceTypeByValue(ReferenceTypeData)
        Console.WriteLine("Called WorkReferenceTypeByValue")
    End Sub

    Public Sub WorkReferenceTypeByReference(ByRef ref As Point)
        Dim b As Point = New Point(4, 4) + ref
        Console.WriteLine("    adding (4,4) to " & ref.ToString)
    End Sub

    Public Sub WorkReferenceTypeByValue(ByVal ref As Point)
        Dim b As Point = New Point(4, 4) + ref
        Console.WriteLine("    adding (4,4) to " & ref.ToString)
    End Sub

    Private m_ReferenceType As Point = New Point(0, 0)
    Public Property ReferenceTypeData As Point
        Get
            Console.WriteLine("  Calling ReferenceTypeData getter")
            Console.WriteLine("  returning: " & m_ReferenceType.ToString)
            Return m_ReferenceType
        End Get
        Set(ByVal value As Point)
            Console.WriteLine("  Calling ReferenceTypeData setter")
            Console.WriteLine("  value = " & value.ToString)
            m_ReferenceType = value
        End Set
    End Property
End Class

The previous code returns to the console the following output

Calling WorkReferenceTypeByReference
  Calling ReferenceTypeData getter
  returning: {X=0,Y=0}
    adding (4,4) to {X=0,Y=0}
  Calling ReferenceTypeData setter
  value = {X=0,Y=0}
Called WorkReferenceTypeByReference
Calling WorkReferenceTypeByValue
  Calling ReferenceTypeData getter
  returning: {X=0,Y=0}
  adding (4,4) to {X=0,Y=0}
Called WorkReferenceTypeByValue

Note the spurious call to the property setter following the method execution. I am supposing this behaviour is produced as a safety measure against inadvertently modifying the underlying property, despite this potentially being the intent.

This behaviour in the case of ByRef vs ByVal usage is easily solved by choosing appropriate ByVal keyword, however hase recently noticed a more insidious behaviour, one that has caused a stack overflow of repeated calls, since the setter call would update a value that called the getter only.

Public Sub DoSomething()
    Dim a As New CustomObject(anotherObject.AProperty(getterArgument))
End Sub

Public Class AnotherObject

    Public Property AProperty as SomeType
        Get
            ' Get value
        End Get
        Set
            ' Set value, call DoSomething
        End Set
    End Property
End Class

In the previous example, calling DoSomething() would fire the AProperty getter method, but then after that usage, would fire the setter method, which by program logic calls DoSomething() again. It is the automatic calling of the setter that puzzles me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

过气美图社 2024-12-16 05:46:38

这实际上是 VB.Net 的一个特性。在您的代码中,您通过引用传递属性,而不是变量。严格来说,传递属性 ByRef 是不可能的,因为 ByRef 需要对变量的引用。但是,编译器会自动代表您创建一个临时本地变量,并将其传递给您的方法。由于该方法可能会更改 ByRef 参数(该参数现在是编译器生成的临时参数,而不是您的属性),因此编译器会插入对 setter 的调用。本质上,会发生类似的情况:

Dim temp = Me.ReferenceTypeData
Me.WorkReferenceTypeByReference(temp)
Me.ReferenceTypeData = temp

其他语言(例如 C#)不允许通过引用传递属性(从参数传递的严格定义来看,这是正确的),而是要求您自己编写与上述代码等效的代码。

This is, in fact, a feature of VB.Net. In your code, you are passing a property, not a variable, by reference. Strictly speaking, passing a property ByRef is not possible, because ByRef needs a reference to a variable. However, the compiler automatically creates a temporary local on your behalf and passes it to the method for you. Because the method may change the ByRef parameter, which is now the compiler-generated temporary and not your property, the compiler then inserts a call to the setter. Essentially, something like this happens:

Dim temp = Me.ReferenceTypeData
Me.WorkReferenceTypeByReference(temp)
Me.ReferenceTypeData = temp

Other languages, such as C#, do not allow passing a property by reference (rightly so from the strict definition of parameter passing) and instead require you to write the equivalent of the above code yourself.

人疚 2024-12-16 05:46:38

它是 VB.net 的一个“功能”。在 C# 中你不会看到这一点。当您使用 ByRef 时,VB.NET 将复制一个对象(在本例中为指针)两次。
请参阅http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/bc54294f-785a-467b-96ec-12d0387074e9/

“据我了解VB。 NET 中,ByRef 会复制参数值两次:一次是在进入方法时,一次是在从方法返回时”,

因此,在方法执行结束时,它会 复制参数值。基本上是复制自身,导致 setter 被调用。

也就是说,对任何对象使用 ByRef 确实没有意义,当您使用 ByVal 时,它只是传递指针,因此能够修改对象的效果是相同的。 ByRef 仅对值类型有用。

It is a VB.net "feature". You won't see this in C#. VB.NET will copy an object (in this case, a pointer) twice when you use ByRef.
See http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/bc54294f-785a-467b-96ec-12d0387074e9/

"As far as I understand VB.NET, ByRef copies a parameter value twice: once when entering the method and once when returning from the method. "

So, at the end of method execution, it is basically copying itself, causing the setter to be called.

That said, there is really no point in using ByRef with any object, it's just passing the pointer anyway when you use ByVal so the effect of being able to modify the object is the same. ByRef is only useful for value types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文