Почему Property Setters вызывается чаще, чем ожидалось? - PullRequest
4 голосов
/ 12 октября 2011

Я наблюдал поведение в VB.net, когда установщики свойств вызываются чаще, чем кажется необходимым, в сочетании с вызовами метода-установщика сестры.

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 vs ByVal легко решается путем выбора подходящего ключевого слова ByVal,однако недавно он заметил более коварное поведение, вызвавшее переполнение стека при повторных вызовах, поскольку вызов установщика обновил бы значение, вызывающее только метод получения.

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, но затем после этого запускает метод установки, который по программной логике снова вызывает DoSomething ().Меня озадачивает автоматический вызов сеттера.

Ответы [ 2 ]

6 голосов
/ 12 октября 2011

Это, по сути, особенность VB.Net. В вашем коде вы передаете свойство, а не переменную, по ссылке. Строго говоря, передача свойства ByRef невозможна, поскольку ByRef нужна ссылка на переменную. Однако компилятор автоматически создает временный локальный файл от вашего имени и передает его методу для вас. Поскольку метод может изменить параметр ByRef, который теперь является временным сгенерированным компилятором, а не вашим свойством, компилятор затем вставляет вызов в установщик. По сути, происходит нечто подобное:

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

Другие языки, такие как C #, не позволяют передавать свойство по ссылке (это справедливо из-за строгого определения передачи параметров) и вместо этого требуют, чтобы вы сами написали эквивалент вышеприведенного кода.

1 голос
/ 12 октября 2011

Это "особенность" VB.net. Вы не увидите этого в C #. VB.NET будет копировать объект (в данном случае указатель) дважды при использовании ByRef. Смотри http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/bc54294f-785a-467b-96ec-12d0387074e9/

"Насколько я понимаю VB.NET, ByRef копирует значение параметра дважды: один раз при входе в метод и один раз при возврате из метода."

Итак, в конце выполнения метода, он в основном копирует себя, вызывая вызов метода установки.

Тем не менее, нет никакого смысла в использовании ByRef с каким-либо объектом, он просто передает указатель в любом случае, когда вы используете ByVal, так что эффект от возможности изменить объект тот же. ByRef полезен только для типов значений.

...