Get up to 80 % extra points for free! More info:

Lesson 10 - Properties in VB.NET

In the previous exercise, Solved tasks for OOP in VB .NET lesson 9, we've practiced our knowledge from previous lessons.

In the previous lesson, Solved tasks for OOP in VB .NET lesson 9, we learned about Shared (static) class members in VB.NET. In today's tutorial, we're going to look at the other class members that we haven't gone over yet.

Properties

We often want to have control over how an object field is being changed from outside of the class. We'd like to set the field as read-only or react to its changes somehow. Let's create a new project (name it Properties) and add the following Student class which will represent a student in a database.

Public Class Student

    Public fullName As String
    Public male As Boolean
    Public age As Integer
    Public fullAged As Boolean

    Public Sub New(fullName As String, gender As Boolean, age As Integer)
        Me.fullName = fullName
        Me.male = gender
        Me.age = age
        fullAged = True
        If age < 18 Then
            fullAged = False
        End If
    End Sub

    Public Overrides Function ToString() As String
        Dim iAmFullAged As String = "I'm"
        If Not fullAged Then
            iAmFullAged = "I'm not"
        End If
        Dim gender As String = "male"
        If Not male Then
            gender = "female"
        End If
        Return String.Format("I'm {0}, {1}. I'm {2} years old and {3} of age.", fullName, gender, age, iAmFullAged)
    End Function

End Class

The class is very simple, the student has a name, gender, and age. The fullAged field is set according to the age and provides a more comfortable determination whether the student is of age from different places in the system. We use a Boolean value to store the gender, True indicates that he's male. The constructor will determine whether the student is of age or not. The ToString() method has been altered to suit our needs. In a real world situation, it'd probably just return the student's name. Let's create a student using the constructor:

Dim s As Student = New Student("Peter Brown", True, 20)
Console.WriteLine(s)

The output:

Console application
I'm Peter Brown, male. I'm 20 years old and I am of age.

Everything looks nice, but the fields can be re-written. We can break the object like this, so it'll no longer function properly (i.e. would have an inconsistent internal state):

Dim s As Student = New Student("Peter Brown", True, 20)
s.age = 15
s.male = False
Console.WriteLine(s)
Console.ReadKey()

The output:

Console application
I am Peter Brown, female. I am 15 years old and I am of age.

Certainly, we would want fullAged to be updated when the age is changed. Aside from that, no other field would need to be altered externally. Students don't usually change their genders or names. However, want to keep the properties accessible for reading, so we can't make them Private. In lessons parts of our course, we've used get methods to read Private fields. We would name them something like GetAge() and so on. We'll create get methods to be able to read certain fields and make these fields Private to prevent them from being modified from the outside. The class would now look something like this (I omitted the constructor and ToString()):

Public Class Student

    Private fullName As String
    Private male As Boolean
    Private age As Integer
    Private fullAged As Boolean

    ...

    Public Function GetFullName() As String
        Return fullName
    End Function

    Public Function GetFullAged() As Boolean
        Return fullAged
    End Function

    Public Function GetAge() As Integer
        Return age
    End Function

    Public Function IsMale() As Boolean
        Return male
    End Function

    Public Sub SetAge(value As Integer)
        age = value
        ' updating whether student is of age
        fullAged = True
        If age < 18 Then
            fullAged = False
        End If
    End Sub

End Class

The methods just returning a value are very simple. In the method setting the age is some more logic, since we have to reconsider the fullAged field. We made sure we can't set variables in any other way than what we want. We now control all the field changes and can work with them as needed. Our design must prevent all unwanted changes of the internal state that would cause an object to malfunction.

Methods for returning values are called getters and methods for setting values are called setters. We could potentially add an EditStudent() method to edit the other fields sort of like the constructor. Essentially, the student's name, age, and other fields would be updated using this method. We could also validate the values being set there since we would be able to handle all attempts to change certain values in one place. Implementing getters and setters manually is without a doubt hard work. Couldn't someone just do it for us? Yep, VB.NET is able to generate them for us! In that case, we're no longer talking about fields, rather properties.

The property syntax is very similar to the field syntax:

Public Property FullName As String

At first, it may seem as if we had declared a field. The property name is capitalized because it is actually a method (2 methods to be precise). We use the Property keyword to declare properties. In the example above, both the setter and the getter would be generated, and the property would be accessible both for reading and writing:

Console.WriteLine(object.FullName) ' reading
object.FullName = "John Black" ' writing

From the outside, the only significant difference against an attribute is that the first letter is uppercase. VB.NET internally generates a Private field and two public methods that are called automatically depending on the context (whether we read or write the value).

If you want to prevent all alteration of your variable from outside the class, you can make the property ReadOnly

Public ReadOnly Property FullName As String = "John Smith"

Unfortunately, this applies for us as well so we won't be able to assign anything to this property, ever from the inside of the class. Therefore, we won't uses that very often.

If we want to implement custom getter and setter, the syntax is as following:

Private _FullName As String
Public Property FullName As String
    Get
        Return _FullName
    End Get
    Set(ByVal value As String)
        _FullName = value
    End Set
End Property

When declaring custom getters and setters, we have to store the value somewhere. We store it into a private attribute which is usually prefixed with an underscore and has the same name as the property has.

If we didn't generate the setter part of the property, there would be no way to change the property from the inside nor the outside. If you want to prevent all alteration of your variable from outside the class, you would just make its setter private:

Private _FullName As String
Public Property FullName As String
    Get
        Return _FullName
    End Get
    Private Set(ByVal value As String)
        _FullName = value
    End Set
End Property

We'll use this very often and most of the properties in our classes will look like this from now on.

Let's take our example of the full-aged problem which must be re-evaluated when the student's age changes:

Private _Age As Integer
Property Age As Integer
    Get
        Return _Age
    End Get
    Set(ByVal value As Integer)
        _Age = value
        ' updating whether student is of age
        fullAged = True
        If _Age < 18 Then
            fullAged = False
        End If
    End Set
End Property

First and foremost, we'll have to create a Private _Age field, the value will be stored there. We'll work with this field in the getter and the setter. If you were to use "Age", without the underscore, in the getter or setter, the program would get stuck in an infinite loop! Why? Well, take another look at the code above (hint: the method we are currently in has already been declared as that).

You cannot implement a custom getter and let the setter be generated automatically. They have to either both be generated automatically or both be implemented manually. To access the value being set in the setter, we use the value parameter. All properties had to be implemented like this until VB.NET added auto-implemented properties in version 4.0. As a matter of fact, we don't need any logic at all in the most of the properties. We'll treat the Age as we would treat a field from now (remember it is case sensitive). Re-assignment of the "Age" triggers the internal logic to update the fullAged field:

object.Age = 15 ' the fullAged field will update immediately as well

Likewise, we could implement a custom getter and log something.

Let's update our Student class so it'll use properties:

Public Class Student

    Private _FullName As String
    Property FullName As String
        Get
            Return _FullName
        End Get
        Private Set(ByVal value As String)
            _FullName = value
        End Set
    End Property
    Private _Male As Boolean
    Property Male As Boolean
        Get
            Return _Male
        End Get
        Private Set(ByVal value As Boolean)
            _Male = value
        End Set
    End Property
    Private _FullAged As Boolean
    Property FullAged As Boolean
        Get
            Return _FullAged
        End Get
        Private Set(ByVal value As Boolean)
            _FullAged = value
        End Set
    End Property

    Private _Age As Integer
    Property Age As Integer
        Get
            Return _Age
        End Get
        Set(ByVal value As Integer)
            _Age = value
            ' updating whether student is of age
            FullAged = True
            If age < 18 Then
                FullAged = False
            End If
        End Set
    End Property

    Public Sub New(fullName As String, gender As Boolean, age As Integer)
        EditStudent(fullName, gender, age)
    End Sub

    Public Sub EditStudent(fullName As String, gender As Boolean, age As Integer)
        Me.FullName = fullName
        Me.Male = gender
        Me.Age = age
    End Sub

    Public Overrides Function ToString() As String
        Dim iAmFullAged As String = "I am"
        If Not FullAged Then
            iAmFullAged = "I am not"
        End If
        Dim gender As String = "male"
        If Not Male Then
            gender = "female"
        End If
        Return String.Format("I am {0}, {1}. I am {2} years old and {3} of age.", fullName, gender, Age, iAmFullAged)
    End Function
End Class

From now on, we will always use properties rather than fields since they allow us to encapsulate objects perfectly. In the .NET framework, all public class members are properties. For example, the Length property of a String. There's a general design guideline that we follow that states: values that are allowed to exit a class must be properties, and values that are internal and "non-editable", must be private fields. Overall, we don't use public fields. The whole class and demo app are, of course, available for download below the article. We can now remove the fullAged checking from the constructor since we now set the age using the Age property, and the FullAged property is updated automatically. Let's try the code which caused issues earlier:

Dim s As Student = New Student("Peter Brown", True, 20)
s.Age = 15
's.Male = False ' This line now causes an error and has to be removed
Console.WriteLine(s)

The output:

Console application
I am Peter Brown, male. I am 15 years old and I am not of age.

If we set the entire property as private, setters and getters will not be able to be marked as public.

In the next lesson, Date and time in VB.NET, we'll learn, how to work with date and time in .NET.


 

Previous article
Solved tasks for OOP in VB .NET lesson 9
All articles in this section
Object-Oriented Programming in VB.NET
Skip article
(not recommended)
Date and time in VB.NET
Article has been written for you by Michal Zurek
Avatar
User rating:
1 votes
Activities