AsValeO
AsValeO

Reputation: 3039

VB.NET Inheritance issue

I've got A base class Base and Sorter and Parser classes derived from it . The same thing with BaseResult with derived SorterResult and ParserResult. Base has a Result field of BaseResult type, BaseResult has a Log field. The reason why I've used a Base class, is because both of Parser and Sorter must write a Log. Here's my code:

Public MustInherit Class Base
    Public Result As BaseResult
    Event LogChanged()
    Protected Sub AddLogLine(ByVal _logString As String)
        If Not String.IsNullOrWhiteSpace(_logString) Then Result.Log.Add(_logString)
        RaiseEvent LogChanged()
    End Sub
End Class

Public Class Sorter
    Inherits Base
    Public Shadows Result As SorterResult
    Sub New()
        Result = New SorterResult With {.Log = New List(Of String)}
    End Sub
    Sub Go()
        AddLogLine("Sorter started")
    End Sub
End Class

Public Class Parser
    Inherits Base
    Public Shadows Result As ParserResult

    Sub New()
        Result = New ParserResult With {.Log = New List(Of String)}
    End Sub
    Sub Go()
        AddLogLine("Sorter started")
    End Sub
End Class

Public MustInherit Class BaseResult
    Public Log As List(Of String)
End Class

Public Class SorterResult
    Inherits BaseResult
    '//SorterResult fields
End Class

Public Class ParserResult
    Inherits BaseResult
    '//ParsedResult fields
End Class

The issue here is that compiler sais(on pic below):enter image description here

"variable 'Result' conflicts with variable 'Result' in the base class 'Base' and should be declared 'Shadows'." When I used Shadows keyword, warning disappeared, but I get a null reference exception on this line, because Result field is Nothing:

If Not String.IsNullOrWhiteSpace(_logString) Then Result.Log.Add(_logString)

I can't assign a value to a Result variable in Base class constructor, because It must be of type SorterResult in Sorter, and ParserResult in Parser. What is the regular pattern here? Sorry my bad english.

enter image description here

Upvotes: 0

Views: 264

Answers (1)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112324

Use generics

Public MustInherit Class Base(Of TResult As BaseResult)
    Public Result As TResult

    Event LogChanged()

    Protected Sub AddLogLine(ByVal _logString As String)
        If Not String.IsNullOrEmpty(_logString) Then Result.Log.Add(_logString)
        RaiseEvent LogChanged()
    End Sub

    Public MustOverride Sub Go()
End Class

Public Class Sorter
    Inherits Base(Of SorterResult)

    Sub New()
        Result = New SorterResult With {.Log = New List(Of String)}
    End Sub

    Public Overrides Sub Go()
        AddLogLine("Sorter started")
    End Sub
End Class

Public Class Parser
    Inherits Base(Of ParserResult)

    Sub New()
        Result = New ParserResult With {.Log = New List(Of String)}
    End Sub

    Public Overrides Sub Go()
        AddLogLine("Sorter started")
    End Sub
End Class

However, this is not a "beautiful" inheritance hierarchy. Inheritance should formulate relations like "a student is a person" where student derives from person. What do sorters and parsers have in common? Are they a Base? Are they loggers? Are they commands (as suggests the Go method)? Is inheritance required here? Wouldn’t it be more appropriate to use aggregation? I would declare a completely independent logger class and inject it into classes. This allows you to be more flexible, as it enables you to inject different types of loggers.

Public MustInherit Class Logger
    Public Event LogChanged()

    Public MustOverride Sub AddLogLine(ByVal message As String)

    Protected Sub OnLogChanged()
        RaiseEvent LogChanged()
    End Sub
End Class

Public Class TextFileLogger
    Inherits Logger

    Public Overrides Sub AddLogLine(ByVal message As String)
        If Not String.IsNullOrEmpty(message) Then
            'TODO: Write message to log file
            OnLogChanged()
        End If
    End Sub
End Class

You can inject it like this:

Public Class SomeConsumerClass
    Private _logger As Logger

    Sub New(ByVal logger As Logger)
        _logger = logger
    End Sub

    Public Sub DoSomething()
        _logger.AddLogLine("Did something!")
    End Sub
End Class

Use like this:

Dim obj As New SomeConsumerClass(New TextFileLogger())
obj.DoSomething()

If you have another kind of logger (XmlFileLogger, StringListLogger, DatabaseLogger...) it is now easy to use it without having to change all the classes using it.

Maybe you should even have only one global logger:

Dim globalLogger As New TextFileLogger()
Dim sorter As New Sorter(globalLogger)
Dim parser As New Parser(globalLogger)

Upvotes: 2

Related Questions