3.2. OOP Development in Visual BasicThe .NET Framework is an OOP-rich development environment. Within that environment, Visual Basic provides access to most OOP features. The primary OOP entity in Visual Basic is the class, but the language also supports two additional variations of this standard entity: (1) the structure, a value-type variation (always derived from System.ValueType) of the normally reference-type class, and (2) modules, a class in which all members are shared and public by default. These three primary development entities, along with a few other entities, such as enumerations, fall under the broad name of type in .NET parlance. Unless otherwise noted, all discussions of class features apply also to structures and modules. 3.2.1. Classes in Visual BasicMost Visual Basic development establishes a one-to-one relationship between a class and a source code file. However, a single file may include multiple classes . Beginning in 2005, the code for a single class may also be split among multiple source code files by using the new Partial keyword. See the entry for that keyword in Chapter 12 for additional information on its usage. The basic source code needed to define a class is pretty simple. Public Class className End Class Once a class is defined, it can be used by creating an instance of the class, which is what is really known as the object. (Some class members can be used without creating an instance; these "shared members" are discussed below.) Instantiating an object requires (1) a variable to hold the object and (2) the creation of the object using the New keyword. These two steps are often performed in two separate VB statements. Dim myInstance As SimpleClass ' Defines the variable myInstance = New SimpleClass ' Creates the object These two steps can be combined into a single statement: Dim myInstance As SimpleClass = New SimpleClass A shortcut syntax makes the instantiation even simpler: Dim myInstance As New SimpleClass 3.2.2. Class MembersVisual Basic classes contain the following types of members:
The following Person class sample illustrates all of the various member types except class nesting. Public Class Person ' ----- Field Members ----- Private fullName As String Private currentAge As Short Public Const MaxAge As Short = 120 ' ----- Event Member ----- Public Event Testing() ' ----- Constructor Method Members ----- Public Sub New() ' ----- Default constructor. fullName = "<unnamed>" End Sub Public Sub New(ByVal newName As String) ' ----- Simple constructor to set an initial field. fullName = newName End Sub ' ----- Method Members ----- Public Sub Test() ' ----- Test the class-defined event. RaiseEvent Testing() End Sub Public Overrides Function ToString() As String ' ----- Returns a friendly string related to the instance. ' NOTE: The 'Overrides' keyword will be discussed ' later in the chapter. Return fullName & ", Age " & currentAge End Function ' ----- Property Members ----- Public Property Age() As Short ' ----- This property performs simple error checking. Get Return currentAge End Get Set(ByVal value As Short) If (value < 0) Or (value > MaxAge) Then Throw New System.ArgumentException( _ "Age ranges from 0 to " & MAX_AGE & ".", "Age") Else currentAge = value End If End Set End Property Public Property Name() As String ' ----- This property adds no special logic; it could ' have been a public field instead. Get Return fullName End Get Set(ByVal value As String) fullName = value End Set End Property End Class 3.2.3. Class Member AccessibilityGenerally, the members of a class constitute that class's public interface. But some members may exist only for the internal use of the class instance itself. Each member of a class includes an access modifier. These special keywords indicate just how visible a particular member is to code outside of the class. Table 3-1 shows the five available access modifiers.
A class itself also has an access modifier, one of Public, Friend, or Private. Public classes can be accessed by another assembly that uses your class' assembly; Friend classes are accessible throughout your assembly, but not outside of it; and Private classes are only accessible within their "declaration context." Generally, Private is similar to Friend, but nested classes can be limited to use only within their parent class by using the Private keyword. 3.2.4. Field MembersVariables, constants, and enumerations declared inside of a class, but outside of any class member procedure, are field members. (Enumerations can also be declared outside of classes altogether.) They are simple to declare and use, as done in the Person class earlier. Private fullName As String Private currentAge As Short Public Const MaxAge As Short = 120 Private field members are often used in tandem with member property procedures to provide logic-controlled access to a data field in the class. Public field members are available through instances of your class. Dim onePerson As New Person MsgBox("Maximum allowed age is " & onePerson.MaxAge & ".") 3.2.5. Event MembersEvents members provide a way to tap into the event-controlled interfaces of the .NET Framework. The declaration and use of events is fully described in Chapter 8. The 2005 release of Visual Basic adds a new feature called custom events that provides more control over the lifetime of an event. This feature is also discussed in Chapter 8. 3.2.6. Method Members and ConstructorsThe function and sub procedures contained within your classes will generally make up the bulk of your Visual Basic application. (Procedures that intercept events are also considered method members.) Methods contain two main parts: (1) the declaration and (2) the body. ' ---- This is the declaration... Public Function AgeInDogYears(sourceAge As Decimal) As Decimal ' ----- ...and this is the body. Return sourceAge * 7@ End Function The declaration of a method is often referred to as its signature. The signature includes the specific argument list and the return value; the method name is not part of the signature. Private methods can only be called within the class itself. Public members can be used within your class or by external users of the class. ' ----- This code resides outside of the class that defines ' the AgeInDogYears function. Dim meAsFido As Decimal meAsFido = theDog.AgeInDogYears(38@) When an object of a particular class is created, the compiler calls a special procedure within the class called a constructor or instance constructor. Constructors initialize an object when necessary. (Constructors take the place of the Class_Initialize event in pre-.NET versions of VB.) Constructor procedures always have a name of New; more than one New procedure may appear in your class, provided each one has a different argument signature. (Normally when two procedures with the same name appear in a class, the Overloads keyworddescribed later in this chaptermust be added to each declaration. However the New procedure is a special case; it does not require the Overloads keyword.) For classes that require no special initialization of their public or private members , the constructor can be omitted from the class; Visual Basic will provide a default constructor when no defined constructor exists in a class. But many classes require some basic initialization, and the constructor is the place to do it. The Person class defined earlier includes two constructors. Public Sub New() ' ----- Default constructor. fullName = "<unnamed>" End Sub Public Sub New(ByVal newName As String) ' ----- Simple constructor to set an initial field. fullName = newName End Sub The first constructor is the default constructor; since it includes no arguments in its declaration signature, it is used by default when an instance is created that lacks any initialization arguments. The second constructor is a custom constructor; it is called when an instance is created that passes a single string argument. ' ----- Uses the default constructor. Dim byDefault As Person = New Person ' ----- Uses the custom constructor. Dim byCustom As Person = New Person("John Q. Public") The arguments included in the instance declaration must match one of the constructor signatures as declared in the class. If a class lacks any constructors, a default constructor is added automatically that does nothing beyond instantiating an object. If you want to force the class to be created with a custom constructor only, add at least one custom constructor to the class. 3.2.7. Property MembersConsider the following simple class. Public Class AnotherPerson Public Name As String Public Age As Short End Class This class includes some of the functionality of the Person class defined earlier. However, the Age property has some problems. Because it is a simple public field, any instance of the class can have its Age field set to any Short value, whether 25, 87, 3349, or -23. Some of these ages are certainly invalid. How do you keep the user from setting the Age field to an invalid value? While you could add specialized function members to set and retrieve the age, .NET includes properties that provide a more elegant solution. Within the class, properties look just like specialized functions; to the user of a class, they look like fields. (When a Visual Basic application is compiled, properties actually become method members.) The Person class defined earlier includes a more protected Age property. Private currentAge As Short ...and later... Public Property Age() As Short Get Return currentAge End Get Set(ByVal value As Short) If (value < 0) Or (value > MaxAge) Then Throw New System.ArgumentException( _ "Age ranges from 0 to " & MAX_AGE & ".", "Age") Else currentAge = value End If End Set End Property The property procedure includes two distinct property accessors, one for setting the hidden tandem value (the Set procedure) and one for retrieving the current value (the Get procedure). You can create a read-only property by supplying only the Get component and adding the ReadOnly keyword to the property definition. Public ReadOnly Property Age() As Short Get Return currentAge End Get End Property The WriteOnly keyword allows you to similarly define a property with only a Set component. New in 2005. The 2005 release of Visual Basic allows you to specify different access levels (such as Public and Friend) to the Get and Set accessors. 3.2.8. Type MembersClasses may include nested classes as needed. Public Class Level1Class Private Class Level2Class ' ----- Add level 2 class code here. End Class ' ----- Add other level 1 class code here. End Class If the nested class is private, it will only be accessible within the outer class. 3.2.9. Instance Members Versus Shared MembersMembers of a class can either be instance members or shared members. Instance members are only useful in a specific instance of the class, that is, from an object. Until an instance of the object exists, these members cannot be used or referenced in any way. Instance members belong to specific instances of the class instead of to the class as a whole. The members added to the sample Person class above are all instance members. Public Class SimpleClass ' ----- This is an instance member. Public Comment As String End Class ... ' ----- In some other code. Dim myInstnace = New SimpleClass myInstance.Comment = "I am not shared!" Shared members (sometimes called static members) can be accessed without the presence of any particular instance of the class. They belong to the whole class, but they are also "shared" among all instances of the class. Shared members are accessed by qualifying the name of the member with the name of the class. Public Class SimpleClass ' ----- This is an instance member. Public Shared Comment As String End Class ... ' ----- In some other code. SimpleClass.Comment = "I am shared!" All members of a Module are automatically shared, even though the Shared keyword is not used on each member of the module. Consider a class that keeps track of how many instances of itself have been created. Public Class Tracker ' ----- Shared variables can be private. Private Shared totalInstances As Integer Public Sub New() ' ----- Each constructor call increments the total. totalInstances += 1 End Sub Public Shared Function GetInstanceCount() As Integer ' ----- Provide read-only access to the count. Return totalInstances End Function Protected Overrides Sub Finalize() ' ----- Decrement the count in the destructor. totalInstances -= 1 MyBase.Finalize End Sub End Class Code such as the following accesses the shared member: Dim firstUse As New Tracker MsgBox(Tracker.GetInstanceCount()) ' --> Displays "1" Dim secondUse As New Tracker MsgBox(Tracker.GetInstanceCount()) ' --> Displays "2" This sample code does have a few issues. Although the Finalize destructor (called when an instance is destroyed, and described more fully later in this chapter) will eventually be called, there is no guarantee that it will be called in a timely manner. Even if a TRacker object goes out of scope or is specifically destroyed by setting the object variable to Nothing, the Finalize method may not be called for quite some time, and the instance count may appear to be inaccurate. Another problem appears because Visual Basic is a multithreaded programming language. If separate threads of your application each create an instance of TRacker at the same time, their respective calls to the New constructor may overlap and produce invalid results. The .NET Framework includes classes that guard against such overlapping code. Mutexes, semaphores, and monitors can be used to manage conflicts between threads in your application. Visual Basic includes a SyncLock statement that also supports some conflict resolution between threads. This statement is described in the SyncLock Statement entry in Chapter 12. 3.2.10. Finalize, Dispose, and Garbage CollectionAn instance of an object can be specifically destroyed by setting the variable that refers to the instance to Nothing. Dim usefulObject As New SimpleClass ... usefulObject = Nothing An object is also automatically destroyed when all variable references to that object go out of scope or otherwise cease to exist. When an object is destroyed using any of these methods, the garbage collection process begins. The .NET Framework includes a garbage collection system that exists to accurately reclaim memory used by objects within .NET applications. When the garbage collector determines that an object is no longer needed, it automatically runs a special destructor method of the class called Finalize. However, there is no way to determine exactly when the garbage collector will call the Finalize method. It will be called at some time in the future, but it may not happen immediately. The .NET Framework uses a system called reference-tracing garbage collection, which periodically releases unused resources according to its schedule, not your program's schedule. Finalize is a Protected method. It can be called from a class and its derived classes, but not from outside the class. (Since the Finalize destructor is automatically called by the garbage collector, a class should never call its own Finalize method directly.) If a class has a Finalize method, that method should in turn explicitly call its base class's Finalize method as well. The general syntax and format of the Finalize method is: Protected Overrides Sub Finalize() ' ----- Cleanup code goes here, and then... MyBase.Finalize() End Sub (The MyBase and Overrides keywords are discussed later in this chapter.) Garbage collection is automatic, and it ensures that unused resources are always released without any specific interaction on the part of the programmer. In most cases, the programmer has no control over the garbage collection schedule; a garbage collection event may occur many minutes after you release an object. This may cause some resources to remain in use longer than necessary. Since some classes may acquire resources that must be released immediately upon completed use of an object instance, .NET supports a "second destructor" called Dispose. Its general syntax and usage is: Class className Implements IDisposable Public Sub Dispose() Implements IDisposable.Dispose ' ----- Immediate cleanup code goes here. End Sub ' ----- Other class code. End Class (The Implements keyword is discussed later in this chapter.) The Dispose method is not called automatically by the .NET Framework. Any code that uses a class with a Dispose method must specifically call that method to initiate the first-level cleanup code. Still, a programmer may forget to call the Dispose method, and resources may be retained until they are fully cleaned up through the Finalize method. 3.2.11. Structures and Modules Versus ClassesIn addition to classes, Visual Basic also supports "structures " and "modules ." (These are somewhat analogous to the VB 6 "Type" and "code module" features.) These two types are really just classes with syntax rules and default behaviors that differ somewhat from standard classes. Structures implement instances of a value type and always derive from System.ValueType. They can never derive from any other base class, nor can a structure be used to derive other structures or classes. The members of a structure cannot specify Protected as an access modifier. Since they are value types, structures are destroyed immediately on disuse; they do not support the Finalize destructor. However, they are lightweight and simple to use for basic data constructs. Structures, when they are not too large, experience some performance increase over equivalent classes. Public Structure SimpleStructure Public Comment As String Public TotalCost As Decimal Public Overrides Function ToString() As String Return Comment & ", " & Format(TotalCost, "$#,##0.00") End Function End Structure Modules are similar to classes that have the Public and Shared keyword added to every member by default (although members can be made Private as well). Since all members of a module are shared, there is no need to create an instance of the module to access the members. In fact, modules cannot be instantiated. They cannot be used to derive other modules or classes, either. Modules can contain nested classes and structures, but modules themselves cannot be nested in any other type. Modules are commonly used for common procedures and global variables that need to be accessed throughout your application. Friend Module GenericCode Public Function CToF(celsius As Decimal) As Decimal ' ----- Convert Celsius to Fahrenheit. Return (celsius * 1.8@) + 32@ End Function End Module 3.2.12. InterfacesVisual Basic implements the object-oriented concept of interfaces through the Interface keyword. Interfaces define the members of a class but not the implementation. They look a lot like classes, but without the member bodies or End constructs (such as End Sub). An interface equivalent to the Person class defined earlier in this chapter might look like the following: Interface IPerson Event Testing() Sub Test() Property Age() As Short Property Name() As String End Interface (By convention, interfaces always begin with the uppercase letter "I.") Interfaces define the public properties, methods, and events of an abstract class. Since interfaces do not support variables, constants, or constructors, some elements of the Person class are missing from this interface definition. Also, since all members of an interface are public by definition, the Public keyword is not needed on each member. Classes implement one or more interfaces through the Implements keyword. This keyword is used in two contexts within the class: (1) at the beginning of the class to declare which interface(s) will be used in the class, and (2) attached to each member that implements a specific member of an interface. Consider the following code. Interface IDog Sub Bark() Sub ScratchFleas() End Interface Interface ICat Sub Meow() Sub DestroyFurniture() End Interface Class MixedUpAnimal Implements IDog Implements ICat Public Sub ScratchFleas() Implements IDog.ScratchFleas ' ----- Add code here. End Sub Public Sub MakeNoise() Implements IDog.Bark, ICat.Meow ' ----- Add code here. End Sub Public Sub Redecorate() Implements ICat.DestroyFurniture ' ----- Add code here. End Sub Public Sub ShowOff() ' ----- Add code here. End Sub End Class This code displays various aspects of interface usage.
While the MixedUpAnimal class implements two distinct interfaces, the term interface also describes the complete set of all public members exposed by this class. This dual use of "interface" is generally not a problem, since when discussing the implementation of a specific interface, the name of that interface is usually included in the discussion. 3.2.13. InheritanceVisual Basic implements OOP inheritance through the Inherits keyword. When a class inherits from a base class, it takes on all public and protected members of that base class; in a way, the derived class is a real implementation of the base class. As an example of inheritance, consider a simple Employee class. Public Class Employee Public FullName As String Private currentSalary As Decimal Public Property Salary() As Decimal ' ----- Salary can be set directly. Get Return currentSalary End Get Set(value As Decimal) currentSalary = value End Set End Property Public Overridable Sub IncSalary(ByVal raisePercent As Decimal) ' ----- Raises given based on a supplied percentage. ' The percent should appear as a decimal percentage, ' as in 0.03 for a 3% raise. currentSalary *= 1@ + raisePercent End Sub End Class This class can be used immediately to manage employee names and salaries. But there may be special salary-related circumstances that apply to specific categories of employees. In this example, all salary increases given to executives include an additional 5 percent increase for a car allowance; secretaries receive an additional 2 percent for an overtime allowance. While distinct classes could be used, inheritance allows all of the classes to still be instances of the Employee class, despite their derived differences. The IncSalary member in the Employee class includes the Overridable keyword. This keyword allows a derived class to modify the implementation of the base class' member. Here are the definitions for the derived Executive and Secretary classes, each of which overrides the base IncSalary member. Public Class Executive Inherits Employee Public Overrides Sub IncSalary(ByVal raisePercent As Decimal) ' ----- Extra 5% for car allowance. Me.Salary *= 1.05@ + raisePercent End Sub End Class Public Class Secretary Inherits Employee Public Overrides Sub IncSalary(ByVal raisePercent As Decimal) ' ----- Extra 2% for overtime allowance. Me.Salary *= 1.02@ + raisePercent End Sub End Class The Me keyword will be discussed in more detail below, but in the code it means, "I'm trying to access members of the current class"in this case, either the Executive or the Secretary class. Since the currentSalary member is private to the Employee class, it can't be accessed directly by the derived classes; all access is made through the public Salary property. Both derived classes include the statement Inherits Employee, which sets up the inheritance relationship from Employee (the base class) to either Executive or Secretary (the derived classes). Each derived instance of the IncSalary class includes the Overrides keyword, which states that this member is specifically overriding an overridable member of the base class. A derived class is not required to override an Overridable member, but it may. Each of these classes can now be used in code, and Visual Basic will call the appropriate class member. Dim worker As New Employee Dim typist As New Secretary Dim ceo As New Executive ' ----- Set the initial salaries. worker.Salary = 30000 typist.Salary = 40000 ceo.Salary = 50000 ' ----- Give everyone a 5% raise. worker.IncSalary(0.05@) typist.IncSalary(0.05@) ceo.IncSalary(0.05@) ' ----- Display the new salaries. MsgBox(worker.Salary) ' --> Displays 31500, a 5% increase MsgBox(typist.Salary) ' --> Displays 42800, a 7% increase MsgBox(ceo.Salary) ' --> Displays 55000, a 10% increase The derived classes each have access to all public members of the base class. ceo.FullName = "Bill Fences" Suppose that, in a more complete employee model, there is a derived class for every type of employee. If each of these derived classes implements its own version of IncSalary, then there is no need for any logic to exist in the IncSalary method of the base Employee class. The code could simply leave the Employee.IncSalary method empty. Visual Basic also allows you to define an abstract member, a member that has no implementation, only a definition (sort of a single-member interface). Each derived class must implement this member to be valid, so VB includes a MustOverride keyword for this purpose. Public MustInherit Class Employee ' ---- Define other members, then... Public MustOverride Sub IncSalary(ByVal raisePercent As Decimal) End Class Members added with the MustOverride keyword do not include a body or an end marker (End Sub, in this case). Visual Basic does not allow a class instance to exist with any abstract members; this semiabstract Employee class can no longer be used to create instances directly. The class can only be used to derive other classes. To state this clearly, the class itself is decorated with the MustInherit keyword. Any class that contains at least one abstract member is termed an abstract class. There may be situations where all members of a class need to be abstract. Such a class (called a pure abstract class) defines an interface, although it is not a true Visual Basic Interface. Consider a Shape class that is designed to model the general properties and actions of geometric shapes (ellipses, rectangles, trapezoids, etc.). All shapes need a Draw method, but the implementation varies, depending on the type of shape. Similarly, methods such as Rotate, translate, and Reflect would each likely require their own shape-specific logic. This Shape class can be implemented as a pure abstract class, from which distinct Ellipse, Rectangle, and other shape-specific classes derive. Public MustInherit Class Shape Public MustOverride Sub Draw() Public MustOverride Sub Rotate(ByVal degrees As Single) Public MustOverride Sub Translate(ByVal x As Single, _ ByVal y As Single) Public MustOverride Sub Reflect(ByVal slope As Single, _ ByVal intercept As Single) End Class Classes can also be defined so that they cannot be used to create new derived classes. The NotInheritable keyword enables this restriction. Public NotInheritable Class UseThisOne ... End Class Non-inheritable classes may not include any abstract members. Visual Basic also includes a NotOverridable keyword that can be used to decorate individual members in a base class. Classes can be derived at any depth. Class A can be derived into Class B, and Class B can further be derived into Class C. Certain rules apply to the inheritance of classes:
3.2.14. MyBase, MyClass, and MeWhen working with derived classes, there are times when references to a member may be somewhat ambiguous; a member name may exist in both the derived class and the base class. Visual Basic provides special keywords to help alleviate this ambiguity. The MyBase keyword provides a reference to the base class from within a derived class. If you want to call a member of the base class from within a derived class, you can use the syntax: MyBase.MemberName This will resolve any ambiguity if the derived class also has a member of the same name. The MyBase keyword can also be used to create an instance of the base class through its constructor: MyBase.New(...) The MyBase keyword cannot be used to access Private members of the base class, as they are inaccessible from derived classes. If a class is derived from a chain of base and derived classes, MyBase looks first to the closet "parent" class in the chain for a matching member (including a matching signature). If a match is not found, VB continues up the chain until the root class, which is always System.Object. The keywords Me and MyClass both provide a reference to the local class (the class in which the current code resides), but they exhibit slight differences. Consider a class named BaseClass and another derived from it, named DerivedClass. Public Class BaseClass Public Overridable Function WhereAmI() As String Return "Base" End Function Public Sub ShowLocation() MsgBox(Me.WhereAmI()) MsgBox(MyClass.WhereAmI()) End Sub End Class Public Class DerivedClass Inherits BaseClass Public Overrides Function WhereAmI() As String Return "Derived" End Function End Class Now consider the following code that uses these classes: Dim firstTry As New BaseClass Dim secondTry As New DerivedClass Dim useAsBase As BaseClass useAsBase = firstTry useAsBase.ShowLocation() ' --> Shows "Base", "Base" useAsBase = secondTry useAsBase.ShowLocation() ' --> Shows "Derived", "Base" The first call to ShowLocation is made using a variable of type BaseClass that refers to an object of type BaseClass. In this case, both of the calls: Me.WhereAmI() MyClass.WhereAmI() return the same value, because they both call WhereAmI in BaseClass. However, in the second case, the variable of type BaseClass holds a reference to an object of DerivedClass. In this case, Me refers to an object of type DerivedClass (the secondTry reference), whereas MyClass still refers to the base class BaseClass (the useAsBase reference). When using the Me keyword, the actual object as originally instantiated is used; when using MyClass, the class of the variable that is used to make the method call becomes the controlling class. 3.2.15. Shadowing and Overloading MembersVisual Basic provides a few additional features that let you provide even more control over which members are used in your base and derived classes. 3.2.15.1. ShadowingShadowing is similar to overriding, but with some very important differences. Consider two classes, BaseClass and DerivedClass: Public Class BaseClass Public simpleField As Integer = 1 Public Overridable Sub TestOverride() MsgBox("BaseClass:TestOverride") End Sub Public Sub TestShadow() MsgBox("BaseClass:TestShadow") End Sub End Class Public Class DerivedClass Inherits BaseClass Public Shadows simpleField As Integer = 2 Public Overrides Sub TestOverride() MsgBox("DerivedClass:TestOverride") End Sub Public Shadows Sub TestShadow() MsgBox("DerivedClass:TestShadow") End Sub End Class BaseClass has two methods, TestOverride (with the Overridable keyword) and TestShadow. DerivedClass also defines methods with the same names; in this case, TestOverride includes the Overrides keyword, and TestShadow uses the Shadows keyword. Both fields also have a related public Integer field. The following code tests the derived class: Dim inUse As DerivedClass = New DerivedClass inUse.TestOverride() inUse.TestShadow() MsgBox("Field = " & inUse.simpleField) Because the object reference inUse is to an object of DerivedClass, the calls to the TestOverride and TestShadow methods, as well as to the public variable simpleField, all refer to code in DerivedClass; the output messages are as expected: DerivedClass:TestOverride DerivedClass:TestShadow Field = 2 The test of the classes working together, though, is a little more interesting: Dim inUse As BaseClass = New DerivedClass inUse.TestOverride() inUse.TestShadow() MsgBox("Field = " & inUse.simpleField) In this case, a variable of type BaseClass refers to an object of type DerivedClass. The output this time is: DerivedClass:TestOverride BaseClass:TestShadow Field = 1 When interacting with base and shadowed members, the type of variable used to reference the members is the deciding factor. In the sample, even though the actual object was of type DerivedClass, the fact that the variable was of type BaseClass caused VB to use the BaseClass version of shadowed features. Class fields, such as simpleField, can only be shadowed; they cannot be overridden. One other difference between shadowing and overriding is that a shadow element need not be the same type of element as its base class partner. For instance, the following code is valid. Public Class BaseClass Public TheShadowKnows As Integer End Class Public Class DerivedClass Inherits BaseClass Public Shadows Sub TheShadowKnows() MsgBox("This code lacks clarity!") End Sub End Class Shadowing only considers the name of the member, not its type or signature. While allowing members of different types to shadow each other seems like a hazardous practice, it actually has its use. In Visual Basic, your code can include a global variable and a local variable of the same name, but of different data types. This ability is possible because the local variable is shadowing its global namesake. In such a case, references to the variable name in the local procedure always refer to the local variable, not the global variable of the same name. This process is known as shadowing by scope. 3.2.15.2. OverloadingOverloading refers to an item being used in more than one way. Generally, overloading occurs when a class includes multiple methods with the same name but with different signatures. For instance, the Abs function in the System.Math class includes several versions, but each uses different source and return data types. Overloads Public Shared Function Abs(Decimal) As Decimal Overloads Public Shared Function Abs(Double) As Double Overloads Public Shared Function Abs(Int16) As Int16 Overloads Public Shared Function Abs(Int32) As Int32 Overloads Public Shared Function Abs(Int64) As Int64 Overloads Public Shared Function Abs(SByte) As SByte Overloads Public Shared Function Abs(Single) As Single Each entry includes the Overloads keyword, which tells VB that this function is overloaded. You can create your own overloaded methods. Consider a function that retrieves a current account balance. The account could be identified either by the customer's account number or driver's license number. The method that retrieves the balance might be defined with two different signatures. Overloads Function GetBalance(accountNumber As Long) As Decimal Overloads Function GetBalance(licenseNumber As String) As Decimal When calling GetBalance, VB decides which version to use based on whether the method is passed a string or a long integer value. New in 2005. The 2005 release of Visual Basic introduced operator overloading to the language. This feature allows a class to define functionality for the standard VB operators, such as the addition operator (+). Operator overloading is discussed in full in Chapter 5. |