Using Shared Methods
Shared members are prevalent in Visual Basic .NET. Many of the classes in the CLR and all the methods in the base class Object are shared. Shared members are commonly employed in utility classes where it's not important to capture state. For example, you don't need to store the state of an integer to calculate the absolute value of an integer.
Classes containing all shared methods are implemented for organizational purposes more than anything. It's easy and convenient to define shared members in a class in an effort to ensure that code is more manageable. The Reset method in Listing 11.3 on lines 4 through 6 demonstrates a shared method.
Shared methods can be added to structures and classes, and it's reasonable to think of a module as a special class containing all shared methods. Expanding the concept of the counter as a means of implementing a self-contained counting mechanism or a way of contriving unique values, the Counter class in Listing 11.4 demonstrates a counter that returns a unique integer, string made up of digits, or a string made up of alphabetic characters .
Listing 11.4 The Counter class uses shared members to return a unique integer, or string of digits or alphabetic characters
1: Public Class Counter 2: 3: Public Shared Function Value(Optional ByVal Reset As Boolean = False, _ 4: Optional ByVal Seed As Long = -1) As Long 5: 6: Static I As Long = Seed 7: If (Reset) Then 8: I = Seed 9: Else 10: I += 1 11: End If 12: 13: Return I 14: End Function 15: 16: Public Shared Function Str(Optional ByVal Reset As Boolean = False, _ 17: Optional ByVal Length As Long = 1) As String 18: 19: Static S As String = New System.String("A", Length) 20: If (Reset) Then 21: S = New System.String("A", Length) 22: Else 23: S = Roll(S, "A", "Z") 24: End If 25: 26: Return S 27: End Function 28: 29: Private Shared Function Chunk(ByVal Str As String) As String 30: Return Right(Str, Str.Length() - 1) 31: End Function 32: 33: Private Shared Function Roll(ByVal S As String, ByVal Low As Char, _ 34: ByVal High As Char) As String 35: If (S.Length() = 0) Then 36: S = Low 37: ElseIf (S.Chars(0) < High) Then 38: S = Chr(Asc(S) + 1) + Chunk(S) 39: Else 40: S = Low + Roll(Chunk(S), Low, High) 41: End If 42: 43: Return S 44: End Function 45: 46: Public Shared Function Digits(Optional ByVal Reset As Boolean = False, _ 47: Optional ByVal Length As Long = 1) As String 48: 49: Static D As String = New System.String("0", Length) 50: If (Reset) Then 51: D = New System.String("0", Length) 52: Else 53: D = Roll(D, "0", "9") 54: End If 55: 56: Return D 57: End Function 58: 59: End Class
The Value function on lines 3 through 14 implements the same behavior as the Counter2 example in Listing 11.3. In addition, you can use optional parameters to reset the value (defined as a static variable in Value), and the optional parameter seed can be used to start counting at an arbitrary. Suppose for example that you wanted a unique identifier for rows in a table. After you closed the table, you could find the highest identifier and start incrementing during the next session from the highest value plus one. Lines 16 through 27 implement a unique string of alphabetic characters from A to Z. Each time the first character reaches A, another character is appended to the string, resulting in a sequence A, B, C, , Z, AA, BA, CA, , ZA, AB, up to a string of about two billion alphabetic characters. Lines 46 through 57 implement an equivalent form of the random string generator using the digits 0 through 9. The Chunk and Roll private functions are reused for the public Str and Digits shared methods.
To use the Str function, call Counter.Str(True) to initialize the static string S to a one-character length string, "A". Each successive call to Counter.Str() returns the next string in the succession.
In production applications, you are likely to encounter classes and structures that have a combination of shared and nonshared methods, as well as utility classes that contain only shared methods.
Listing 11.5 contains the complete listing for the form and the Rects class.
Listing 11.5 Shared utility methods in a Rects class and a form that uses GDI+ to display random rectangle subdivisions
1: Public Class Form1 2: Inherits System.Windows.Forms.Form 3: 4: [ Windows Form Designer generated code ] 5: 6: Private Sub MenuItem2_Click(ByVal sender As System.Object, _ 7: ByVal e As System.EventArgs) Handles MenuItem2.Click 8: Application.Exit() 9: End Sub 10: 11: Public Shared Function GetAboutText() As String 12: Return "Rectangles Demo" & vbCrLf & _ 13: "Copyright 2001. Paul Kimmel." & vbCrLf & _ 14: "Visual Basic.NET Unleased" 15: End Function 16: 17: Private Sub MenuItem6_Click(ByVal sender As System.Object, _ 18: ByVal e As System.EventArgs) Handles MenuItem6.Click 19: MsgBox(GetAboutText(), MsgBoxStyle.Information, "About Rectangles") 20: End Sub 21: 22: Private Function RandomPens() As Pen 23: 24: Randomize() 25: 26: Select Case Rnd() * 6 27: Case Is < 1 : Return Pens.AliceBlue 28: Case Is < 2 : Return Pens.Red 29: Case Is < 3 : Return Pens.Bisque 30: Case Is < 4 : Return Pens.Blue 31: Case Is < 5 : Return Pens.Plum 32: Case Else 33: Return Pens.DarkGoldenrod 34: End Select 35: 36: End Function 37: 38: Private Sub MenuItem4_Click(ByVal sender As System.Object, _ 39: ByVal e As System.EventArgs) Handles MenuItem4.Click 40: 41: Dim I As Integer 42: Dim G As System.Drawing.Graphics = CreateGraphics() 43: 44: Refresh() 45: 46: Dim Segments As Integer 47: For I = 0 To 25 48: Randomize() 49: Segments = CInt(Rnd() * 100) 50: 51: G.DrawRectangle(RandomPens(), _ 52: Rects.RandomRectangle(ClientRectangle, Rnd() * Segments, _ 53: Segments + 1)) 54: Next I 55: 56: End Sub 57: 58: End Class 59: 60: ' Rectangle.vb - Defines several rectangle sub-division shared methods 61: ' Copyright 2001. All Rights Reserved. 62: ' By Paul Kimmel. firstname.lastname@example.org 63: 64: Imports System.Drawing 65: 66: Public Class Rects 67: 68: Public Shared Function RandomRectangle(ByVal Rect As Rectangle, _ 69: ByVal Index As Integer, ByVal Segments As Integer) As Rectangle 70: 71: If (Index Mod 3 = 0) Then 72: Return RectangleSector(Rect, Index, Index / 2, Segments) 73: ElseIf (Index Mod 2 = 0) Then 74: Return VerticalRectangle(Rect, Index, Segments) 75: Else 76: Return HorizontalRectangle(Rect, Index, Segments) 77: End If 78: 79: End Function 80: 81: Public Shared Function RectangleSector(ByVal Rect As Rectangle, _ 82: ByVal X As Integer, ByVal Y As Integer, _ 83: ByVal Segments As Integer) As Rectangle 84: Dim R As Rectangle 85: R = VerticalRectangle(Rect, Y, Segments) 86: R = HorizontalRectangle(R, X, Segments) 87: Return R 88: 89: End Function 90: 91: Public Shared Function VerticalRectangle(ByVal Rect As Rectangle, _ 92: ByVal Index As Integer, ByVal Segments As Integer) As Rectangle 93: 94: Return New Rectangle(Rect.Left, NewTop(Rect, Index, Segments), _ 95: Rect.Width, NewBottom(Rect, Index, Segments)) 96: 97: End Function 98: 99: Public Shared Function HorizontalRectangle(ByVal Rect As Rectangle, _ 100: ByVal Index As Integer, ByVal Segments As Integer) As Rectangle 101: 102: Return New Rectangle(NewLeft(Rect, Index, Segments), Rect.Top, _ 103: NewRight(Rect, Index, Segments), Rect.Bottom) 104: 105: End Function 106: 107: Public Shared Function NewTop(ByVal Rect As Rectangle, _ 108: ByVal Index As Integer, ByVal Segments As Integer) As Integer 109: 110: Return (Rect.Top + (Index / Segments) * Rect.Height) 111: 112: End Function 113: 114: Public Shared Function NewBottom(ByVal Rect As Rectangle, _ 115: ByVal Index As Integer, ByVal Segments As Integer) As Integer 116: 117: Return NewTop(Rect, Index, Segments) + (Rect.Height / Segments) 118: 119: End Function 120: 121: Public Shared Function NewLeft(ByVal Rect As Rectangle, _ 122: ByVal Index As Integer, ByVal Segments As Integer) As Integer 123: 124: Return Rect.Left + (Index / Segments) * Rect.Width 125: 126: End Function 127: 128: Public Shared Function NewRight(ByVal Rect As Rectangle, _ 129: ByVal Index As Integer, ByVal Segments As Integer) As Integer 130: 131: Return NewLeft(Rect, Index, Segments) + Rect.Width / Segments 132: 133: End Function 134: 135: End Class
To shorten the listing, the 135 lines of code shown in Listing 11.5 conceal the generated code. Listing 11.5 demonstrates the interplay between an application (although a trivial one) and shared methods in a class. The code shows two units, Form1.vb and Rectangles.vb. Rectangles.vb begins on line 60 and continues to line 135. As you can determine from the listing, the class Rects in Rectangles.vb maintains no state; Rects simply calculates subdivisions of rectangles. The methods were originally implemented with creating custom components . The rectangles were used to calculate bounding regions for complex visual controls. In the example, each time the user presses F5, the program draws 26 rectangles on Form1 (see lines 46 through 54). The example also provides further examples of using a Graphics object (lines 42 and 51) from GDI+, the global Pens class, which contains shared Pen objects (beginning on line 22),and several examples of shared methods. (Refer to Chapter 17, "Programming with GDI+," for more information on graphics programming.)