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.
精彩评论