periblepsis
periblepsis

Reputation: 139

VB.NET programming patterns for late binding of parsers for varying dataset file formats

synopsis

It's been a long time since writing VB.NET code and since then .NET CORE has been created and grown. So quite some time. And programming practices and patterns have evolved without my keeping up, I'm sure.

So I'm feeling a bit ignorant. I believe I need late binding. But I'd like to know if there is a preferred pattern that achieves my goals. Or if there is something very wrong with the approach I finally chose.

details

I have decades of saved monthly datasets from NOAA and elsewhere. While the parameter values that I care about have persisted throughout almost a century, the formats have evolved over the years. (For example, there was a time when the information was manually entered from old sheets into ASCII, a time when FORTRAN formatting was used, a time when web pages existed, etc.)

I have a folder with many of these monthly datasets stored under Windows. And I have written parser functions for each kind of formatting in my possession. But I also need to anticipate the addition of still other formats, past, present, and future, which are laid out differently and yet still contain the information I'd like to process. So I expect to add more parsers as time passes.

As I uncover new formats and write and add new parsers to the project, I'd like to simply add another .vb file without modifying existing, working code. If anything is to break, it should only be the new parser class I'm writing.

I feel this means using some form of late binding, with a managing function that is able to track down the appropriate parser.

The approach I took (and works) is to create a MustInherit base class that imposes an interface, which allows over-loading. Derivative classes will be written, one for each type of formatting, implementing the interface regardless of the original formatting. Each derivative class will feature a Shared function which is its relevant parser and is able to indicate success or failure by either returning Nothing or else a fully constructed class (with interface.)

The MustInherit base class, ClimateMonthlyDataset, also provides this Shared function to identify and apply the appropriate parser to the indicated file (a "reader" is passed to it):

Public Shared Function CreateClimateDataset(FileContents As IEnumerable(Of String)) As ClimateMonthlyDataset
    Dim PossibleClimateDataDactoryClasses() As Type = GetType(MyApplicationContext).Assembly.GetTypes()
    For Each q As Type In PossibleClimateDataDactoryClasses
        If q.IsClass AndAlso (q.Attributes And TypeAttributes.Abstract) = 0 AndAlso q.GetInterface("IClimateDataset") IsNot Nothing Then
            Dim TryMonthlyClimateDataFactory As MethodInfo = q.GetMethod("CreateNewDataset")
            If TryMonthlyClimateDataFactory IsNot Nothing AndAlso (TryMonthlyClimateDataFactory.Attributes And MethodAttributes.Static) <> 0 Then
                Dim MonthlyClimateData As Object = TryMonthlyClimateDataFactory.Invoke(Nothing, New Object() {FileContents})
                If MonthlyClimateData IsNot Nothing Then
                    Return CType(MonthlyClimateData, ClimateMonthlyDataset)
                End If
            End If
        End If
    Next
    Return Nothing
End Function

I also considered the idea of using custom attributes to decorate and use reflection on those attributes, instead. But the interface is a requirement. So that already exists. It seemed to me that adding an attribute, as well, just unnecessarily duplicates knowledge and adds still more code to maintain.

I didn't see added value there.

summary

Is there a better approach that I missed considering?

Is there something terribly wrong with the approach I did take?

Upvotes: 1

Views: 42

Answers (0)

Related Questions