Enumerable of String version 3 – Generics to simplify

I continue exploring readability and the framework by investigating the possibility of something similar to an enumerable but with strings. It really is quite different than an enumerable in several ways. I had two previous posts V1 and V2 . I’m going to repeat some information in this post. You can see I’m beginning to call this a Completion List Pattern.

Story

I was on a project where I am reading information from a mainframe travel agency system. I communicate back and forth using strings that represent screens that the travel agent used to see before the internet. These are similar to the 3270 displays but most are smaller. An example would be the inventory screen. The travel agent would type in a hotel and some dates and they would receive a screen indicating what days were available, closed or on request. On a month availability screen there are a lot of letters. under each day for a hotel there is one of three letters. If you have to call the hotel to request a date the letter R was there to indicate that it was on request. If the hotel was closed to arrivals there there was a letter C on that day. If the hotels availability was open and you could come and say then an O was under that day. These letters are called inventory codes.

For working with this data I wanted to put it in proper objects. Link makes it trivial to write queries to find what you want once you have it is objects. I really like enums (enums) because you get the intellisense but the only integer types were allowed for the values. I then had to have a separate object that mapped the integers to the strings found on the screen. That visually separated the string from the enums which complicated upkeep. I’ve been experimenting and here is what I have come up with. It’s not ideal but I learned a lot putting it together.

Goals:

  • Easily map string values to types for parsing
  • Provide intellisense with a statement completion list of values for easy coding
  • Minimize the amount of code needed to implement the pattern

Let’s begin by creating a type to store our inventory codes.

''' <completionlist cref="InventoryStatusCodes"/>
Public Class InventoryCode
    Inherits completionListItem
    Public Sub New(ByVal SetText As String)
        MyBase.New(SetText)
    End Sub
    Public Sub New()
        MyBase.New()
    End Sub
End Class

This is actually far more complex than I wanted but apparently you can’t inherit constructors (See my previous post about my question on stackoverflow.com). Any ideas on how to shrink this would be appreciated.

Next we need to list out all the codes in a completion list.

Public NotInheritable Class InventoryCodes
    Inherits completionList
    Public Shared ReadOnly Open As New InventoryCode("O")
    Public Shared ReadOnly Closed As New InventoryCode("C")
    Public Shared ReadOnly OnRequest As New InventoryCode("R")
End Class

That has a lot of words in it but it’s easy enough to follow. Let’s see how the intellisense works

image

Great. Now we can get to business. Let’s assume that I have a variable with data from the screen called ScreenText. I can check to see if it’s available by the following:

If ScreenText = InventoryCodes.Open.Text Then
    Console.WriteLine("Yay! We can stay at the hotel!")
End If

That’s Easy. Now something more difficult. Let’s say I want to convert a screen text directly into the proper Inventorycode type. You could use reflection to iterate through the items to find the right one. The problem with reflection is the speed loss. To mitigate that I created a generic class that created a dictionary once then you can use it several times. I also included a command to find the right item sing that is code you will need to do a lot. First you create a new completionListDictionairy object like below to generate the dictionary and give you the tools. Below I put the proper InventoryCode object in the variable “I”.

Dim InventoryDictionairy As New completionListDictionairy(Of InventoryCode, InventoryCodes)
Dim ScreenText As String = "O"
Dim I As InventoryCode
I = InventoryDictionairy.GetValueFromString(ScreenText)

You can now even loop through all the codes and print them out if you like:

Console.WriteLine(InventoryDictionairy.items.Count)
For Each item In InventoryDictionairy.items
    Console.WriteLine(item.Key.ToString & " : " & item.Value.Text)
Next

Let’s see how this meets our goals

  • Easily map string values to types for parsing : Seems pretty easy after some setup
  • Provide intellisense with a statement completion list of values for easy coding: Intellisense is there
  • Minimize the amount of code needed to implement the pattern: Umm… lots more code than I like so I didn’t really succeed here.

Let me know what you think in the comments but please be kind. I know that this is not production code and probably does not really justify it’s work. It’s an experiment.

Namespace CompletionListHelperNamespace
    Public Class completionListDictionairy(Of t As {completionListItem, New}, ts As {completionList, New})
        Public items As New Dictionary(Of String, t)

        'Get the code from the list created in the new
        Public Function GetValueFromString(ByVal Text As String) As t
            For Each code In items
                If code.Value.Text = Text Then Return code.Value
            Next
            Return Nothing
        End Function

        'Use reflection to put the codes in a list for later fast retrieval
        Sub New()
            Dim TempeStrClass As New ts
            Dim TempeStr As New t
            Dim TypeStrClass As Type = TempeStrClass.GetType
            Dim TypeStr As Type = TempeStr.GetType
            Dim f() As FieldInfo = TypeStrClass.GetFields
            For Each Field In f
                If Field.GetValue(Field).GetType Is TempeStr.GetType Then
                    items.Add(Field.Name, Field.GetValue(Field))
                End If
            Next
        End Sub
    End Class

    Public Class completionListItem
        Public ReadOnly Text As String
        Public Sub New(ByVal SetText As String)
            Text = SetText
        End Sub
        Public Sub New()
        End Sub
    End Class

    Public Class completionList
    End Class
End Namespace