It is generally desirable to designate the member variables of a class as Private (using the Private keyword). This means that only member methods of that class can access their value. You make member variables private to support data hiding, which is part of the encapsulation of a class.
Typically, most methods will be public (designated by the Public keyword). The public members of your class constitute a contract between your class and the clients of your class. Any object that interacts with your class is a client. Your public methods promise that if the client calls them with the right parameters, they will perform the promised action. How your methods perform that object is not part of the public contract. That is up to your class.
You can also have some private methods, known as helper methods, whose job it is to do work for methods of your class, but which are not available to clients. The private member variables and private methods are not part of your public contract; they are hidden details of the implementation of your class.
Object-oriented programmers are told that member variables should be private. That is fine, but how do you provide access to this data to your clients? The answer for VB.NET programmers is properties.
Properties allow clients to access class state as if they were accessing member fields directly, while actually implementing that access through a class method.
This is ideal. The client wants direct access to the state of the object. The class designer, however, wants to hide the internal state of his class (perhaps in class fields), and provide indirect access through a method. The property provides both: the illusion of direct access for the client, the reality of indirect access for the class developer.
By decoupling the class state from the method that accesses that state, the designer is free to change the internal state of the object as needed. When the Time class is first created, the Hour value might be stored as a member variable. When the class is redesigned, the Hour value might be computed, or retrieved from a database. If the client had direct access to the original Hour member variable, the change to computing the value would break the client. By decoupling and forcing the client to go through a property, the Time class can change how it manages its internal state without breaking client code.
In short, properties provide the data hiding required by good object-oriented design. Example 5-10 creates a property called Hour, which is then discussed in the paragraphs that follow.
|
Option Strict On Imports System Public Class Time ' private member variables Private mYear As Integer Private mMonth As Integer Private mDayOfMonth As Integer Private mHour As Integer Private mMinute As Integer Private mSecond As Integer Property Hour( ) As Integer Get Return mHour End Get Set(ByVal Value As Integer) mHour = Value End Set End Property ' public accessor methods Public Sub DisplayCurrentTime( ) Console.WriteLine( _ "{0}/{1}/{2} {3}:{4}:{5}", _ mMonth, mDayOfMonth, mYear, Hour, mMinute, mSecond) End Sub 'DisplayCurrentTime ' constructors Public Sub New(ByVal dt As DateTime) mYear = dt.Year mMonth = dt.Month mDayOfMonth = dt.Day mHour = dt.Hour mMinute = dt.Minute mSecond = dt.Second End Sub 'New Public Sub New( _ ByVal mYear As Integer, _ ByVal mMonth As Integer, _ ByVal mDayOfMonth As Integer, _ ByVal mHour As Integer, _ ByVal mMinute As Integer, _ ByVal mSecond As Integer) Me.mYear = mYear Me.mMonth = mMonth Me.mDayOfMonth = mDayOfMonth Me.Hour = mHour Me.mMinute = mMinute Me.mSecond = mSecond End Sub 'New End Class 'Time Module Module1 Sub Main( ) Dim currentTime As DateTime = DateTime.Now Dim time1 As New Time(currentTime) time1.DisplayCurrentTime( ) 'extract the hour to a local variable Dim theHour As Integer = time1.Hour 'display the local variable Console.WriteLine("Retrieved the hour: {0}", _ theHour) 'add one to the local variable theHour += 1 'write the time back to the object time1.Hour = theHour 'display the result Console.WriteLine("Updated the hour: {0}", _ time1.Hour) End Sub End Module Output: 5/1/2002 8:56:59 Retrieved the hour: 8 Updated the hour: 9
You create a property with this syntax:
Property Identifier( ) As Type Get statements End Get Set(ByVal Value As Type) statements End Set End Property
If you create the property in Visual Studio .NET however, the editor will provide extensive help with the syntax. For example, once you type:
Property Minute As Integer
the IDE will reformat your property as follows:
Property Minute( ) As Integer Get End Get Set(ByVal Value As Integer) End Set End Property
In Example 5-10, Hour is a property. Its declaration creates two accessors: Getand Set.
Property Hour( ) As Integer Get Return mHour End Get Set(ByVal Value As Integer) mHour = Value End Set End Property
Each accessor has an accessor-body that does the work of retrieving and setting the property value. The property value might be stored in a database (in which case the accessor would do whatever work is needed to interact with the database), or it might just be stored in a private member variable (in this case, mHour):
Private mHour As Integer
The body of the Get accessor is similar to a class method that returns an object of the type of the property. In Example 5-10, the accessor for the Hour property is similar to a method that returns an integer. It returns the value of the private member variable mHour in which the value of the property has been stored:
Get Return mHour End Get
In this example, the value of mHour is returned, but you could just as easily retrieve an Integer value from a database or compute it on the fly.
Whenever you reference the property (other than to assign to it), the Get accessor is invoked to read the value of the property. For example, in the following code the value of the Time object's Hour property is assigned to a local variable. What actually happens is that the Get accessor is called, which returns the value of the Hour member variable, and that value is assigned to the local variable named theHour:
Dim time1 As New Time(currentTime) Dim theHour As Integer = time1.Hour
The Set accessor sets the value of a property. Set has an implicit parameter, Value, that represents the assigned value. That is, when you write:
Minute = 5
the compiler passes the value you are assigning (5) to the Set statement as the Value parameter. You can then set the member variable to that value using the keyword:
mMinute = Value
The advantage of this approach is that the client can interact with the properties directly, without sacrificing the data hiding and encapsulation sacrosanct in good object-oriented design.
At times you may want to create a property that allows the client to retrieve a value but not to set it. You can mark your property ReadOnly, as in the following:
ReadOnly Property Hour( ) As Integer
Doing so allows you (and forces you) to leave out the Set accessor in your property. If you do add a Set accessor, the compiler will complain with the message:
Properties declared 'ReadOnly' cannot have a 'Set'
If you leave out the Set accessor and then try to assign to the property, the compiler will complain with the message:
Property 'Hour' is 'ReadOnly'
In short, marking the property ReadOnly enlists the compiler in enforcing that you can not use that property to set a value.
Similarly, you can mark a property WriteOnly:
WriteOnly Property Hour( ) As Integer
Doing so will cause the compiler to enforce that your property must have a Set and must not have a Get accessor. If you leave out the Get or Set without marking the property WriteOnly or ReadOnly, respectively, you will receive a compile error.
You are not permitted to combine ReadOnly with WriteOnly, but this is not much of a burden.
Top |