为什么Property Setters比预期更频繁地被调用?

时间:2011-10-12 16:10:45

标签: vb.net properties getter-setter

我在VB.net中观察到一种行为,其中属性设置器的调用次数比看似必要的更多,并且调用了对姐妹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

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

ByRef vs 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()。这是塞特犬的自动召唤让我感到困惑。

2 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:1)

这是一个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仅对值类型有用。