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 firstname.lastname@example.org.