开发者

Why do Property Setters get called more often than expected?

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 Su开发者_JAVA百科b

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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜