VB.NET Call Setter from within Getter
I have a class like this:
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
The Getter is calculating average and calls Setter to save the value. For some reason I cannot understand, the average is always 0. For example:
Dim c As New Class2()
c.Add(1)
c.Add(2)
c.Add(3)
Console.WriteLine(c.开发者_开发知识库Avg.ToString()) ' This will print 0
Did I do something wrong? What is the cause of this?
Wow, I think you've discovered a very strange behavior of VB: when you are inside the definition of a function, you can return a value either with Return
or by using =
to "set" the function's value.
Like this:
Function GetInteger() As Integer
GetInteger = 5
End Function
In the above function, the line GetInteger = 5
is basically equivalent to Return 5
*.
OK, so you probably already knew that. But here's the weird part, and I had no idea this was the case until testing it just now (admittedly, on Mono, but I am seeing the same behavior you are): apparently this applies to property getters as well. So look at this line:
Avg = If((_intList.Count > 0), _sum / _intList.Count, _avg)
You're actually not calling the property setter there; you're setting the return value for the getter. You can verify this by removing the line Return _avg
; suddenly you will see your getter starts returning the actual average.
*Not exactly the same, as you could later set GetInteger
to something else without returning immediately whereas using Return
ensures the function returns right away.
This is by design and explicitly mentioned in the Visual Basic Language Specification, chapter 9.7.1:
A special local variable, which is implicitly declared in the Get accessor body's declaration space with the same name as the property, represents the return value of the property. The local variable has special name resolution semantics when used in expressions. If the local variable is used in a context that expects an expression that is classified as a method group, such as an invocation expression, then the name resolves to the function rather than to the local variable. For example:
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
Solve your issue by assigning the _avg field directly. Property getters with side-effects like this is best avoided.
Your setters and getters really should just be returning properties, and not doing the calculations themselves. Try creating a method like calcAvg()
that does the average calculations, and on the Add()
call that method (which should internally not re-perform the whole average calculation, but simply update it. Let me know if you're not sure how to do that). That calcAvg()
method will set the _avg
instance variable.
Also, I'm not sure it really makes send to have a setter for the average. An average of numbers is a derived property, not something that should be set by an external user.
MrDanA answer is more correct than what I'm going to give you, however, I believe the reason why you are getting the value of 0 is because you are never setting the variable _avg to anything. After you do your AVG calculation if you do:
_avg = AVG return _avg
I get a value of 2 when I do this.
Like I said before though... MrDanA's answer is a better way to go.
精彩评论