VB.NET Call Setter из Getter

У меня есть такой класс:

Public Class MyClass

Private _intList As New List(Of Integer)
Private _avg As Decimal

Public Sub Add(ByVal anInt As Integer)
    _intList.Add(anInt)
End Sub

Public Property Avg() As Decimal
    Get
        Dim _sum As Integer = 0
        For Each anInt In _intList
            _sum += anInt
        Next
        Avg = If((_intList.Count > 0), _sum / _intList.Count, _avg)
        Return _avg
    End Get
    Set(ByVal value As Decimal)
        If _avg <> value Then
            _avg = value
            Console.WriteLine("Value changed")
        End If
    End Set
End Property

End Class

Getter вычисляет среднее значение и вызывает Setter для сохранения значения. Я почему-то не могу понять, среднее всегда 0. Например:

Dim c As New Class2()
c.Add(1)
c.Add(2)
c.Add(3)
Console.WriteLine(c.Avg.ToString()) ' This will print 0

Я сделал что-то не так? Какова причина этого?


person Cam L    schedule 29.07.2011    source источник
comment
каково значение Avg после запуска: If((_intList.Count › 0), _sum / _intList.Count, _avg).   -  person webdad3    schedule 29.07.2011
comment
Ответ @Dan Tao правильный, но я надеюсь, что это всего лишь пример кода, потому что Avg должно быть свойством только для чтения, которое просто возвращает _avg. Вы можете добавить грязный бит в метод Add для пересчета _avg, но в противном случае нет причин публично раскрывать свойство установки.   -  person Chris Haas    schedule 29.07.2011


Ответы (4)


Это задумано и явно упомянуто в Спецификации языка Visual Basic, глава 9.7.1:

Специальная локальная переменная, неявно объявленная в пространстве объявления тела средства доступа Get с тем же именем, что и у свойства, представляет возвращаемое значение свойства. Локальная переменная имеет специальную семантику разрешения имен при использовании в выражениях. Если локальная переменная используется в контексте, ожидающем выражение, которое классифицируется как группа методов, например выражение вызова, то имя разрешается в функцию, а не в локальную переменную. Например:

ReadOnly Property F(i As Integer) As Integer
    Get
        If i = 0 Then
            F = 1    ' Sets the return value.
        Else
            F = F(i - 1) ' Recursive call.
        End If
    End Get
End Property

Решите проблему, назначив поле _avg напрямую. Лучше избегать таких геттеров с побочными эффектами.

person Hans Passant    schedule 29.07.2011
comment
+1. Выбор дизайна для обратной совместимости с VB6 и более ранними версиями, когда это был единственный способ вернуть значение из Property Get (оператор Return был добавлен в VB7). - person MarkJ; 29.07.2011

Вау, я думаю, вы обнаружили очень странное поведение VB: когда вы находитесь внутри определения функции, вы можете вернуть значение либо с помощью Return или с помощью = "установить" значение функции.

Нравится:

Function GetInteger() As Integer
    GetInteger = 5
End Function

В приведенной выше функции строка GetInteger = 5 в основном эквивалентна Return 5*.

Итак, вы, вероятно, уже знали это. Но вот странная часть, и я понятия не имел, что это так, пока не протестировал его только сейчас (правда, на Mono, но я вижу то же поведение, что и вы): очевидно, это относится и к получателям свойств. Итак, посмотрите на эту строку:

Avg = If((_intList.Count > 0), _sum / _intList.Count, _avg)

На самом деле вы не вызываете установщик свойства здесь; вы устанавливаете возвращаемое значение для геттера. Вы можете убедиться в этом, удалив строку Return _avg; вдруг вы увидите, что ваш геттер начинает возвращать фактическое среднее значение.

*Не совсем то же самое, так как позже вы могли бы установить GetInteger на что-то другое без немедленного возврата, тогда как использование Return гарантирует, что функция вернется сразу.

person Dan Tao    schedule 29.07.2011
comment
Хороший улов, не заметил! - person Chris Haas; 29.07.2011

Ваши сеттеры и геттеры действительно должны просто возвращать свойства, а не выполнять сами вычисления. Попробуйте создать такой метод, как calcAvg(), который выполняет расчет среднего значения, а в Add() вызовите этот метод (который не должен внутренне выполнять повторно весь расчет среднего значения, а просто обновлять его. Дайте мне знать, если вы не знаете, как это сделать ). Этот метод calcAvg() установит переменную экземпляра _avg.

Кроме того, я не уверен, что это действительно заставляет посылать сеттер для среднего значения. Среднее числовое значение — это производное свойство, а не то, что должен задавать внешний пользователь.

person MrDanA    schedule 29.07.2011

Ответ MrDanA более правильный, чем то, что я собираюсь вам дать, однако я считаю, что причина, по которой вы получаете значение 0, заключается в том, что вы никогда не устанавливаете переменную _avg для чего-либо. После того, как вы выполните расчет AVG, если вы:

_avg = средний возврат _avg

Когда я это делаю, я получаю значение 2.

Как я уже говорил, хотя ... Ответ MrDanA - лучший путь.

person webdad3    schedule 29.07.2011