Defining Structures

Team-Fly    

 
Visual Basic .NET Unleashed
By Paul Kimmel
Table of Contents
Chapter 5.  Subroutines, Functions, and Structures

Defining Structures

Chapter 2 introduced the structure as the replacement for the Type construct. Types only supported the aggregation of data; the Structure construct is more closely related to the class construct than the Type construct.

Structures support fields, properties, events, and methods . This section will demonstrate how to define structures and how to implement each of the new capabilities of structures. The last subsection discusses features that are not supported in Structure constructs, which comprise the differences between structures and classes.

Use the Structure construct where you would have used a Type in VB6, keeping in mind all the additional capabilities afforded by the new construct.

Defining Fields and Properties

Fields are data members of aggregate types. Generally fields are Private members representing the underlying value of a property. Properties are generally used to represent the public means of referencing a field.

The distinction between field and property exists because it was decided years ago that the most reliable way to constrain data use was to access data through methods. Properties are really methods that look like data.

You will learn more about the Private and Public keywords in Chapter 7. For now, suffice to say that members of a structure (or class) can have an access specifier , including Private or Public, and these keywords constrain how consumers can use these members, or if consumers can even use members.

Private members can't be used by consumers of a structure, but public members can be. Listing 5.15 demonstrates defining a private field in a structure and allowing access to that field using a public property.

Listing 5.15 Private fields and public properties
  1:  Public Structure Description  2:   3:  Private FEyeColor As EyeColor  4:   5:  Public Property EyeColor() As EyeColor  6:  Get  7:  Return FEyeColor  8:  End Get  9:   10:  Set(ByVal Value As EyeColor)  11:  FEyeColor = Value  12:  End Set  13:  End Property  14:   15:  End Structure 

Line 3 defines the field FEyeColor. The F-prefix identifies the name as a field, and more importantly, dropping the F yields a perfect name for the associated property, EyeColor. This pairing of fields and properties makes it very easy and convenient to choose field and property names and keep the associations simple. This convention allows us to program quickly and concisely.

Lines 5 through 13 demonstrate the basic syntax of a property statement. Notice that a single property statement, containing a getter and setter, is distinct from the two individual property statements employed in VB6. Read Chapter 7 for complete coverage of the property idiom.

Listing 5.15 demonstrates a basic use of fields and properties. Although EyeColor can be used just like data, the Get and Set blocks work just like proceduresyou can write any validation code (or other code) that helps you constrain the way the data is used.

Adding Structure Methods

The Structure construct supports methods too. Thus you can add functions and subroutines to your structures, associating behaviors with your aggregate, structure data types.

Suppose we add two more fields and their associated properties to our Description to support storing a first and last name with the description. We might want to implement a function to return a formatted full name rather than requiring the user to enter a full name. Also, we decide that we might want the full name displayed in first-name-first or first-name-last order. Listing 5.16 demonstrates one complete possible revision, adding the fields, properties, and new method.

Listing 5.16 Adding methods to structures
  1:  Public Structure Description  2:   3:  Private FEyeColor As EyeColor  4:  Private FFirstName, FLastName As String  5:   6:  Public Property EyeColor() As EyeColor  7:  Get  8:  Return FEyeColor  9:  End Get  10:   11:  Set(ByVal Value As EyeColor)  12:  FEyeColor = Value  13:  End Set  14:  End Property  15:   16:  Public Property FirstName() As String  17:  Get  18:  Return FFirstName  19:  End Get  20:  Set(ByVal Value As String)  21:  FFirstName = Value  22:  End Set  23:  End Property  24:   25:  Public Property LastName() As String  26:  Get  27:  Return FLastName  28:  End Get  29:  Set(ByVal Value As String)  30:  FLastName = Value  31:  End Set  32:  End Property  33:   34:  Private Function FirstNameOrder() As String  35:  Return FirstName & " " & LastName  36:  End Function  37:   38:  Private Function LastNameOrder() As String  39:  Return LastName & ", " & FirstName  40:  End Function  41:   42:  Public Function FullName(Optional ByVal UseFirstNameOrder _  43:  As Boolean = True) As String  44:  If (UseFirstNameOrder) Then  45:  Return FirstNameOrder()  46:  Else  47:  Return LastNameOrder()  48:  End If  49:  End Function  50:   51:  End Structure 

The revisions include the fields FFirstName and FLastName on line 4, the FirstName and LastName properties on lines 16 to 32, and the three methods, FirstNameOrder, LastNameOrder, and FullName from lines 34 through 49. The public method is a function that returns the formatted full name based on the Optional parameter UseFirstNameOrder. FirstNameOrder and LastNameOrder are Private methods because they present partial implementation of the function FullName. Making FirstNameOrder and LastNameOrder private simply keeps consumers from having to worry about two methods. All a consumer needs to worry about is the FullName method and the order desired. This constitutes a subjective implementation choice; a reasonable person could have made FirstNameOrder and LastNameOrder public and dispensed with the FullName method altogether. Generally, I try to keep public methods to a minimum, but that is a preference rather than a rule.

As you can see from Listing 5.16, structures can become quite complex.

Implementing Constructors

As you learned in Chapter 2, all structures are System.ValueType entities. All value types implicitly have a default, parameterless constructor.

Structure variables can be declared the same way as intrinsic types, like Integers, without the New keyword, or, you can declare Structures with the New keyword if you've defined a constructor that takes parameters and want to define an instance of the structure using the parameterized constructor. Whether you use the New keyword or not when declaring structure instances, structures are still value types as opposed to reference types. Keep in mind that the distinction between a value type and reference type is copy- by-value versus copy-by-reference when instances are assigned, as depicted earlier in Figure 5.3.

Default Constructor

From Chapter 2, you know that a constructor is a special method responsible for initializing new instances. In Visual Basic .NET, constructors are defined as the subroutine New(). Constructors may or may not have parameters and can be overloaded. For example, in the following declaration, the variable varname represents any valid variable name and type represents any valid data type, including user-defined types:

 Dim  varname  As New  type  () 

This example explicitly invokes the New() constructor, although it may look as if you are calling a procedure type() .

You can't inherit from structures. All structures get a single default constructorSub New()from their common ancestor ValueType, and you can define parameterized constructors. You can't overload the default constructor, as all Structures are implicitly declared with the NotInheritable keyword. Refer to Chapter 7 for more on classes and declaration attributes like NotInheritable, and refer to the section "Unsupported Structure Features" for more information on features that aren't supported when working with structures.

When you declare a structure variable, you are implicitly calling the empty constructor Sub New(); you can also explicitly construct a Structure by typing Dim MyStructure As New StructureType(), where StructureType is a user-defined structure. You must use the New keyword to invoke parameterized constructors.

Parameterized Constructors

Suppose we want to define a constructor for the Description structure that takes a first and last name. The facts that we want to initialize two fields and that they are both strings determine what the parameters for the constructor will be:

 Public Sub New(ByVal AFirstName As String, ByVal ALastName As String)   FFirstName = AFirstName   FLastName = ALastName End Sub 

By convention, I matched the parameter names to the fields they will initialize by using the same root name with an A-prefix for the parameters and simply assigning the param-eters to the associated field names.

To construct Description structures using the new constructor, you will have to use the Form of the Dim statement that uses the New keyword.

Note

Constructor calls in Visual Basic .NET are a little confusing. The way New is used makes New look like a modifier on the type and the type actually look like the method call. For example, New Description("Noah", "Kimmel") looks like we are invoking a method Description with the modifier New. The C++ language treats New as an operator and constructors always have the same name as the class (or struct, in C++). Hence, if Description were defined as a type in C++, Description would actually be the constructor.

Just keep in mind that in Visual Basic .NET, New is actually the constructor but the parameters are placed in parentheses after the type. Only Microsoft knows why New is used this way.


 Dim MyDescription As New Description("Paul", "Kimmel") 

The preceding statement actually calls the New subroutine defined at the beginning of this subsection.

Defining Structure Events

You can define events for structures, but you can't associate an event with a procedure using the WithEvents statement and the Handles clause. To define events for structures and associate them with an event handler, you need to complete several specific steps; we'll use the Description structure as a frame of reference to demonstrate:

  1. Add an event statement to the Description structure. Whenever an attribute of a description changes, we will raise a Changed event passing the instance of the initiating structure. (Add the following statement to the definition of Description: Public Event Changed(ByVal ADescription As Description). Because we can't subclass structures, we will use the specific type rather than the base class Object type for the Changed event.)

  2. In each of the setters for Description, write a RaiseEvent statement, RaiseEvent Changed(Me), passing the reference to itself. (Refer to Chapters 8 and 9 for much more information on events and delegates.)

  3. In the class that will handle Changed events, define a subroutine with the same signature and, by convention, the same name as the event. (The example simply shows the FullName of the changed Description.)

     Public Sub Changed(ByVal ADescription As Description)   MsgBox(ADescription.FullName & " changed") End Sub 
  4. After a Description structure is created, use the AddHandler method to associate the event handler with the event.

     AddHandler FDescription.Changed, AddressOf Changed 

    Assuming that we used the same name of the event as the name of the handler and we have a variable FDescription defined, the preceding code is sufficient.

Listing 5.17 shows all the revisions to a data entry form and the Description structure necessary to implement and respond to Description.Changed events. Code not involved in the revision was hidden using code outlining to keep the listing from getting out of hand.

Listing 5.17 Defining and responding to Structure events
  1:  Public Class Form1  2:   3:  [...]  4:   5:   Private FDescription As Description   6:  [...]  7:   8:  Private Sub Assign()[...]  9:   10:  Private Sub Button1_Click(ByVal sender As System.Object, _ [...]  11:   12:  Private Sub InitializeEyeColors()[...]  13:   14:   Private Sub Changed(ByVal ADescription As Description)   15:   MsgBox(ADescription.FullName & " changed")   16:   End Sub   17:   18:  Private Sub Initialize()  19:   AddHandler FDescription.Changed, AddressOf Changed   20:  InitializeEyeColors()  21:  End Sub  22:   23:  Private Sub Form1_Load(ByVal sender As System.Object, _  24:  ByVal e As System.EventArgs) Handles MyBase.Load  25:   26:  Dim D As New Description()  27:   Initialize()   28:   29:  End Sub  30:   31:  Private Sub Button2_Click(ByVal sender As System.Object, _[...]  32:   33:  End Class  34:   35:  Public Enum EyeColor[...]  36:   37:  Public Structure Description  38:   39:  [...]  40:   Public Event Changed(ByVal ADescription As Description)   41:   42:  Public Property EyeColor() As EyeColor  43:  [...]  44:   45:  Set(ByVal Value As EyeColor)  46:  FEyeColor = Value  47:       RaiseEvent Changed(Me)   48:  End Set  49:  End Property  50:   51:  Public Property FirstName() As String  52:  [...]  53:   54:  Set(ByVal Value As String)  55:  FFirstName = Value  56:       RaiseEvent Changed(Me)   57:  End Set  58:  End Property  59:   60:  Public Property LastName() As String  61:  [...]  62:   63:  Set(ByVal Value As String)  64:  FLastName = Value  65:   RaiseEvent Changed(Me)   66:  End Set  67:  End Property  68:   69:  [...]  70:   71:  End Structure 

You've seen a lot of this code in previous listings in this section, so those parts not related to the topic of this section were hidden with code outlining (introduced in Chapter 1). All of the lines specifically part of the declaration, raising, or handling of the event are in boldface for your convenience.

Beginning at the top of the listing, each aspect of incorporating the Structure event is defined. Line 5 declares a Description variable named FDescription. Lines 14 through 16 define the event handler in the Form1 class using the same name as the event in the Description structure on line 40. Line 19 adds the handler Form1.Changed to the Description.Changed invocation list. Line 27 calls the Initialize method to initialize various aspects of the form. (This strategy is employed by convention.) Line 40 defines the event for the Description structure; this syntax is identical to the syntax used for event definitions in any entity. Lines 47, 56, and 65 raise the event in the setters (property Set methods) for the EyeColor, FirstName, and LastName properties, respectively.

As you can determine from Listing 5.17, defining events and event handlers for structures is identical to defining them for classes and modules. The biggest difference is that only classes support the WithEvents statement and Handles clause. Events in structures and modules are associated with handlers using the AddHandler statement.

Declaring Structure Variables

Events are value types, so they are declared like intrinsic types with the access specifier or Dim, followed by a name for the structure, the keyword As, and the structure name. As demonstrated in the previous section, you can also create an instance of a structure using the New keyword. To initialize a structure using a parameterized New constructor, you must use the New keyword. By default, the non-parameterized constructor defined in the ValueType class is invoked when you declare a structure.

ValueType is the immediate ancestor of structures. Structures aren't inheritable and you can't override the constructor defined in ValueType. More information on structure-related features that are not supported is upcoming in the "Unsupported Structure Features" section.

Information Hiding

Information hiding is an aspect of object-oriented programming. The premise is based on the idea that too much information is a bad thing when trying to solve problems. The affirmative principle is divide et impera, or divide and conquer.

The access specifiers Public and Private are supported for structures. Public members can be referenced by consumers; private members can only be referenced internally. (The Protected and Protected Friend access specifiers aren't supported because they relate to inheritance, which isn't supported for structures.)

Structure Arguments and Return Types

VB6 limits passing and returning structure types. You may only define structures as private members in VB6, and only pass and return structures between private members in VB6. These limitations severely inhibit the utility of UDTs in VB6.

Visual Basic .NET supports passing and returning structures in both public and private methods, and you can define nested structures (that is, types in classes) or standalone structures.

Using the Description structure introduced in Listing 5.15, the following subroutine and function are legal Visual Basic .NET code but would cause a compiler error in VB6 unless both the structure and the procedures were defined in a module or declared private in a form or class:

 Public Structure Description[...] Private FDescription As Description Public Function GetDescription() As Description   Return FDescription End Public Sub SetDescription(ByVal Value As Description)   FDescription = Value End Sub 

The first statement represents the definition of our structure from Listing 5.15, Description. The second statement defines a variable of type Description. Both the function and subroutine are public. GetDescription returns a public structure and SetDescription accepts a public structure. The equivalent code isn't supported in VB6. Public UDTs in Visual Basic .NET make the structure much more powerful than the Type capabilities from VB6.

Unsupported Structure Features

Structures are not classes. Although the Visual Basic .NET structure is more closely related to the C++ struct idiom and the Visual Basic .NET class, there are some aspects of classes not supported in structures.

You may use a structure in new code for several reasons. Probably the most common example is to support legacy code, like the Windows API. However, structures aren't classes, and aside from a minor convenience in their declaration, you should consider using a class instead of a structure for new code. In fact, there is even a refactoring technique named "Replace Record with Data Class" that discusses the motivation for replacing classes with structures. The biggest reason to use structures is if some conjoined aspect of your program requires them, but in most cases new aggregate types should be defined as classes.

The following features are not supported by structures:

  • Structures implicitly inherit from System.ValueType but can't inherit from any other type.

  • Structures implicitly use the attribute NotInheritable; structures can't inherit from other structures.

  • You can't define a parameterless constructor; you can define a parameterized constructor.

  • You can't define a destructor for structures; destructors are represented by the Dispose method.

  • Data members of structures can't have initializers, nor can they use the New keyword. You have to define a parameterized constructor to initialize objects or you have to initialize objects after a structure variable is declared, external to the structure.

  • Members of a structure are public by default.

Structures are value types. When you assign structures, the code actually performs a memberwise copy of the members of the structure. Object assignment performs a reference assignment. Consequently, passing and returning structure variables will incur more overhead than using instances of classes.


Team-Fly    
Top
 


Visual BasicR. NET Unleashed
Visual BasicR. NET Unleashed
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 222

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net