Search code examples
vb.netclassencapsulation

What is the purpose of declaring a Class within another Class?


I come from the VBA world where options to breakdown your code into classes, namespaces, and modules is limited. Now I just landed in a world where the options are many, and I feel lost.

I would like to know what is the purpose of declaring a Class within another Class? (see example below)

Class FirstClass
    Public OnePropertyInside As String
    Class SecondClass
        Public AnotherProperty As String
    End Class
End Class

If I create a new instance of FirstClass (say myFirstClass), SecondClass is not instantiated. Even more bizzare (to me at least), is that intelissense offers me myFirstClass.SecondClass. Obviously, because the class is not instantiated, I cannot access any of its members.


So, is that usefull only if the SecondClass contains shared members? To try answering that question I added a shared member within SecondClass:

Class FirstClass
    Public OnePropertyInside As String
    Class SecondClass
        Public AnotherProperty As String
        Public Shared SharedProperty As String
    End Class
End Class

I ran a few tests which brought secondary questions (see comments in code)

Sub Main()       

    Dim myFirstClass As New FirstClass
    'Works as expected
    Console.WriteLine(myFirstClass.OneProperty)

    'What is the difference between the two lines below?
    Console.WriteLine(myFirstClass.SecondClass.SharedProperty)
    Console.WriteLine(FirstClass.SecondClass.SharedProperty)

    'This line cannot be compiled, this demonstrates SecondClass is not instantiated when FirstClass is.
    Console.WriteLine(myFirstClass.SecondClass.AnotherProperty)

    Dim mySecondClass As New FirstClass.SecondClass
    'Works as expected, but I feel this hierarchy should better be dealt with through a namespace statement?
    Console.WriteLine(mySecondClass.AnotherProperty)

End Sub

Solution

  • When you do that, and the inner class is accessible to other classes (it's accessibility is Public or Friend), the outer class basically just works like a namespace. So for instance, using your example, you could create a new object of the nested class without ever creating one of the outer class:

    Dim x As New FirstClass.SecondClass()
    

    The most obvious benefit is the structural organization of the code, much like namespaces and code files. So, for instance, it's not uncommon to use nested classes for constants, to help better organize them:

    Public Class Urls
        Public Class Processing
            Public Const Submit As String = "..."
            Public Const Cancel As String = "..."
        End Class
    
        Public Class Reporting
            Public Const Daily As String = "..."
            Public Const Weekly As String = "..."
        End Class
    End Class
    
    ' ...
    
    Dim url As String = Urls.Reporting.Daily
    

    However, outside of the narrow set of situations where things like that are useful, most people would prefer to not nest public classes at all.

    However, as others have mentioned, the one place where you really will see nested classes used fairly regularly is for Private ones. If you need some small helper class which will have no use to code outside of your class, there's no reason to expose it. Even if you set it's accessibility to Friend, it will still be visible to all the other classes in the same project. Therefore, if you really want to hide it from everything else, you'll want to make it a nested private class. For instance:

    Public Class MyClass
        Public Function GetTheIdOfSomething() As Integer
            Dim d As Details = GetDetailsAboutSomething()
            If d.Value Is Nothing Then
                Return d.Id
            Else
                Throw New Exception()
            End If
        End Sub
    
        Private Function GetDetailsAboutSomething() As Details
            ' ... return a Details object
        End Function
    
        Private Class Details
            Public Property Id As Integer
            Public Property Value As String
        End Class
    End Class