The power and expressiveness of Visual Basic.Net represents a marked difference between versions 6 and 7 (VB.NET). The fundamental idioms have change moderately on the surface. The basic grammar has changed only a little making VB.NET code recognizable as Visual Basic. However, it is the more advanced idioms that have changed significantly, making VB.NET an expressive and powerful development language. One such idiom is the nested class. Visual Basic.Net supports nested classes. Although the nested class idiom is not a common-place idiom, nested classes do demonstrate on facet of the breadth and depth of changes in VB.NET that make it a first class language.
A nested class is quite simply a class defined within a class. Technically, wherever you can use a nested class you could use a non-nested class; thus the reason for employing the nested class idiom is a semantic reason. You can and would employ a nested class in VB.NET when you a body of code, including some number of methods, fields, properties, and events, that describe an entity in the solution domain and do not make sense outside of the enclosing class. You might also use a nested class if you need to capture multiple snapshots of state internally. Keep in mind that either of these problems can be implemented using every day aggregation relationships. Actually employing the nested class idiom is a semantic election and in this instance, demonstrates a new capability of Visual Basic.Net.
While writing this article a problem that naturally presents itself if how to model a book, perhaps for managing digital e-books. A book is comprised of a table of contents, index, perhaps a bibliography, and chapters. Each of these entities represents a class; further we could argue that none of these elements makes sense out of the context of book. Finally, in the case of the chapter, a book will likely contain multiple chapters. As a result table of contents, index, bibliography, and chapter all represent good classes, and chapters could be organized into a statically sized array or collection of chapter objects. Listing 1 demonstrates a solution to the e-book problem using VB.NET nested classes.
Listing 1: Implementing a solution for representing the relationship between a book and its contents.
1: Public Class EBook
2:
3: #Region ” Public Members ”
4:
5: Public Property Title() As String
6: Get
7: Return FTitle
8: End Get
9: Set(ByVal Value As String)
10: FTitle = Value
11: End Set
12: End Property
13:
14: Public ReadOnly Property Table() As String
15: Get
16: Return CType(Elements(0), TableofContents).Content
17: End Get
18: End Property
19:
20: Public Sub New()
21: MyBase.New()
22: Elements = New ArrayList()
23: Elements.Add(New TableofContents(“Table of Contents”, “”))
24: End Sub
25:
26: Public Sub Dispose()
27: Static Disposed As Boolean = False
28: If (Disposed) Then Exit Sub
29: Disposed = True
30: Elements = Nothing
31: End Sub
32:
33: Private Sub Increment()
34: If (FIndex < Elements.Count) Then
35: FIndex += 1
36: Else
37: Beep()
38: End If
39: End Sub
40:
41: Public Function MoveNext() As Boolean
42: Increment()
43: Return FIndex < Elements.Count
44: End Function
45:
46: Public Sub Reset()
47: FIndex = -1
48: End Sub
49:
50: Public ReadOnly Property ChapterTitle() As String
51: Get
52: Return Current.Title
53: End Get
54: End Property
55:
56: Public ReadOnly Property ChapterContent() As String
57: Get
58: Return Current.Content
59: End Get
60: End Property
61:
62: Private ReadOnly Property Current() As Chapter
63: Get
64: Return Elements(FIndex)
65: End Get
66: End Property
67:
68: Private Sub Decrement()
69: If (FIndex > 1) Then
70: FIndex -= 1
71: Else
72: Beep()
73: End If
74: End Sub
75:
76: Public Function MovePrevious() As Boolean
77: Decrement()
78: Return FIndex >= 0
79: End Function
80:
81: Public Sub AddChapter(ByVal Title As String, _
82: ByVal Content As String)
83: Elements.Add(New Chapter(Title, Content))
84: FIndex = Elements.Count
85: End Sub
86: #End Region
87:
88: #Region ” Protected Members ”
89: Protected Overrides Sub Finalize()
90: Dispose()
91: MyBase.Finalize()
92: End Sub
93: #End Region
94:
95: #Region ” Private Members ”
96: Private Elements As ArrayList
97: Private FTitle As String
98: Private FIndex As Long = 0
99: #End Region
100:
101: #Region ” Nested Classes ”
102:
103: Protected MustInherit Class BookElement
104: Protected FTitle As String
105: Protected FContent As String
106:
107: Public Sub New()
108: MyBase.New()
109: End Sub
110:
111: Public Sub New(ByVal theTitle As String, _
112: ByVal theContent As String)
113:
114: MyBase.New()
115: FTitle = theTitle
116: FContent = theContent
117: End Sub
118:
119: Public ReadOnly Property Title() As String
120: Get
121: Return FTitle
122: End Get
123: End Property
124:
125: Public MustOverride ReadOnly Property Content() As String
126:
127: Public ReadOnly Property PageCount() As Integer
128: Get
129: Return GetPageCount()
130: End Get
131: End Property
132:
133: Private Function GetPageCount() As Integer
134: Return Len(Content) / 500
135: End Function
136: End Class
137:
138: Protected Class Chapter
139: Inherits BookElement
140:
141: Public Overrides ReadOnly Property Content() As String
142: Get
143: Return FContent
144: End Get
145: End Property
146:
147: Public Sub New(ByVal theTitle As String, _
148: ByVal theContent As String)
149:
150: MyBase.New(theTitle, theContent)
151: End Sub
152:
153: End Class
154:
155: Protected Class TableofContents
156: Inherits BookElement
157:
158: Public Overrides ReadOnly Property Content() As String
159: Get
160: Return FContent
161: End Get
162: End Property
163:
164: Public Sub New()
165: MyBase.New()
166: End Sub
167:
168: Public Sub New(ByVal theTitle As String, _
169: ByVal theContent As String)
170: MyBase.New(theTitle, theContent)
171: End Sub
172:
173: Public Sub AddHeader(ByVal Title As String)
174: ‘ Add Element to Table of Contents
175: End Sub
176: End Class
177:
178: #End Region
179:
180: End Class
The listing is long, illustrating how nested classes can become quite long very quickly. To organize the class I used the #Region pragma to create outlined code regions. The EBook class is ordered by consumer interest. The public members are listed first followed by the protected, private, and nested members.
The nested elements of EBook are defined from lines 101 to 178. The first nested member is a nested abstract class BookElement. BookElements are defined to have an element title, some content, and span some number of pages. Two BookElement classes are defined: Chapter beginning on line 138 and TableOfContents beginning on line 155. Both Chapter and TableOfContents inherit from BookElement and must implement the abstract virtual property Content—on line 125—indicated by the MustOverride modifier. Implementing Content as a polymorphic property allows you to implement Content to return differently formatted text data. For example, TableOfContents might return text that is chapter titles with the appended starting page of the chapter, and the Contents property for a chapter might be formatted as Rich Text with page numbers. If you have a property or method with the MustOverride method then you must use the MustInherit class modifier, line 103. MustInherit classes are virtual abstract classes; virtual abstract classes are very similar to the intent of COM interfaces. A virtual abstract class is used to guide the implementation of child classes.
The EBook class is defined to internally manage chapters. When a consumer adds a chapter, a good implementation would be to update the TableofContents class and dynamically calculate page numbers based on the span of pages in the TableOfContents and all of the chapters.
There is a lot of extra code. We might want to move the navigation methods to a separate class of implement the IEnumerate interface. What should be apparent by the code listing is that Visual Basic.Net supports a diverse variety of idioms and allows you to write significantly more expressive code. Line 16 demonstrates dynamic type-checking. Lines 14 to 18 demonstrate a read only property. Check out lines 20 through 24 for an example of a VB.NET constructor, and Line 26 through 31 demonstrates a conventionally convenient Dispose method. An overloaded destructor is defined on lines 89 to 92. Lines 111 to 117 demonstrates a parameterized constructor.
Combining the new idioms and expanded grammar, Visual Basic.Net has a larger vocabulary, allowing your code to be more expressive. Finding a suitable balance between the new idioms, like nested classes, and aggressively managing code complexity is an implicit objective you with have to tackle.
About the Author
Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. He is the founder of Software Conceptions, Inc, founded in 1990. Paul Kimmel performs contract software development services in North America and can be contacted at [email protected].