locked
Const Array alternatives? Shared MustOverride won't work RRS feed

  • Question

  • I've been trying to find a good alternative to Constant Arrays for a bunch of strings that pair up with Enums (fail codes, application processing states, etc.) So I tried:

    Enum OpResults
        NoSourceFile
        SheetNotWorksheet
        SelectionNotRange
    End Enum
    
    Public MustInherit Class EnumTxt
        Const NoEnumTxt = "(no text available)"
        Friend Shared myDict As Dictionary(Of [Enum], String)
        Public Shared ReadOnly Property Txt(key As [Enum]) As String
            Get
                If myDict Is Nothing Then LoadDict()
                If myDict.ContainsKey(key) Then
                    Txt = myDict(key)
                Else
                    Txt = NoEnumTxt
                End If
            End Get
        End Property
    
        MustOverride Sub LoadDict()
    End Class
    
    Public Class OpResultsTxt
        Inherits EnumTxt
        Overrides Sub LoadDict()
            myDict = New Dictionary(Of [Enum], String) From {
                {OpResults.NoSourceFile, "No source file associated with this workbook"},
                {OpResults.SheetNotWorksheet, "Current sheet not worksheet"},
                {OpResults.SelectionNotRange, "Current selection not a range"}
            }
        End Sub
    End Class
    
    ... txt = OpResults.Txt(OpResult)
    
    

    The problem is that, in the base class, the LoadDict() call can't work without the LoadDict() being shared, and Shared can't be MustOverride. So I can't get there from here.

    I could just instance the string classes. But they are truly shared; instancing has no meaning. I could just repeat all the code in the base class for each of the sets of Enum-related strings. For obvious reason, this seems like a bad idea.

    Is there a better way (a way at all?) to create these arrays of constant strings--tied to the related Enum values? I want to isolate the strings from code as much as possible, make them as easy to reference as a constant, and tie them to the Enums in case they change.

    Tuesday, December 18, 2018 3:14 AM

Answers

  • You can add attributes to Enums and retrieve them.  The attributes can be System defined or custom.  Here is how using the Systems Description attribute.

    First add these imports

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    Imports System.Reflection

    Then change the enum to have the attribute

        Public Enum OpResults
            <Description("No source file associated with this workbook")> NoSourceFile
            <Description("Current sheet not worksheet")> SheetNotWorksheet
            <Description("Current selection not a range")> SelectionNotRange
        End Enum

    Then a helper to get the attribute

        Public Shared Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
            Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
            Dim attr() As DescriptionAttribute
            attr = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute),
                                                     False), DescriptionAttribute())
    
            If attr.Length > 0 Then
                Return attr(0).Description
            Else
                Return EnumConstant.ToString()
            End If
        End Function

    When that is done you can see it work with this

            Dim foo As OpResults = OpResults.SheetNotWorksheet
            Dim desc As String = GetEnumDescription(foo)
    
            foo = OpResults.NoSourceFile
            desc = GetEnumDescription(foo)

    Custom attributes using the same imports

        Public Enum EnumWithCustomAttr
            <AttrsOfEnumWithCustomAttr(1, "One")>
            Member1 = 1
            <AttrsOfEnumWithCustomAttr(2, "two")>
            Member2 = 2
            <AttrsOfEnumWithCustomAttr(-3, "this is three")>
            Member3 = 3
        End Enum
    
        <AttributeUsage(AttributeTargets.Field)>
        Public Class AttrsOfEnumWithCustomAttr : Inherits System.Attribute
            Public SomeValue As String
            Public SomeNum As Integer
            Public Sub New(num As Integer, Valu As String)
                Me.SomeNum = num
                Me.SomeValue = Valu
            End Sub
        End Class
    
        Public Shared Function MyEnumCustomAttr(ByVal EnumConstant As EnumWithCustomAttr) As AttrsOfEnumWithCustomAttr
            Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
            Dim sa As AttrsOfEnumWithCustomAttr = DirectCast(fi.GetCustomAttributes(GetType(AttrsOfEnumWithCustomAttr), True)(0), AttrsOfEnumWithCustomAttr)
            Return sa
        End Function
    
    
    '  TO USE
    
            Dim bar As EnumWithCustomAttr = EnumWithCustomAttr.Member3
            Dim a As AttrsOfEnumWithCustomAttr = MyEnumCustomAttr(bar)
    

    Hope it helps.


    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it."

    - from former MSDN User JohnWein

    SerialPort Info

    Multics - An OS ahead of its time.


    • Edited by dbasnett Tuesday, December 18, 2018 5:50 PM
    • Marked as answer by Dick Watson Tuesday, December 18, 2018 6:22 PM
    Tuesday, December 18, 2018 2:13 PM

All replies

  • try  to fix the  code:

    Public Class Form1
        Friend Shared myDict As Dictionary(Of [Enum], String)
        Public Enum OpResults
            NoSourceFile
            SheetNotWorksheet
            SelectionNotRange
        End Enum
        Public Class EnumTxt
    
            Public Shared t As New OpResultsTxt
            Const NoEnumTxt = "(no text available)"
    
            Public Shared ReadOnly Property Txt(key As [Enum]) As String
                Get
                    If myDict Is Nothing Then t.LoadDict()
                    If myDict.ContainsKey(key) Then
                        Txt = myDict(key)
                    Else
                        Txt = NoEnumTxt
                    End If
                End Get
            End Property
    
            Public Overridable Sub LoadDict()
    
            End Sub
    
        End Class
    
        Public Class OpResultsTxt
            Inherits EnumTxt
            Overrides Sub LoadDict()
                myDict = New Dictionary(Of [Enum], String) From {
                {OpResults.NoSourceFile, "No source file associated with this workbook"},
                {OpResults.SheetNotWorksheet, "Current sheet not worksheet"},
                {OpResults.SelectionNotRange, "Current selection not a range"}
            }
            End Sub
        End Class
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim re As New OpResults
            ...txt=OpResultsTxt.Txt(re)
        End Sub
    
    End Class
    

    Best Regards,

    Alex


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, December 18, 2018 7:53 AM
  • You can add attributes to Enums and retrieve them.  The attributes can be System defined or custom.  Here is how using the Systems Description attribute.

    First add these imports

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    Imports System.Reflection

    Then change the enum to have the attribute

        Public Enum OpResults
            <Description("No source file associated with this workbook")> NoSourceFile
            <Description("Current sheet not worksheet")> SheetNotWorksheet
            <Description("Current selection not a range")> SelectionNotRange
        End Enum

    Then a helper to get the attribute

        Public Shared Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
            Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
            Dim attr() As DescriptionAttribute
            attr = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute),
                                                     False), DescriptionAttribute())
    
            If attr.Length > 0 Then
                Return attr(0).Description
            Else
                Return EnumConstant.ToString()
            End If
        End Function

    When that is done you can see it work with this

            Dim foo As OpResults = OpResults.SheetNotWorksheet
            Dim desc As String = GetEnumDescription(foo)
    
            foo = OpResults.NoSourceFile
            desc = GetEnumDescription(foo)

    Custom attributes using the same imports

        Public Enum EnumWithCustomAttr
            <AttrsOfEnumWithCustomAttr(1, "One")>
            Member1 = 1
            <AttrsOfEnumWithCustomAttr(2, "two")>
            Member2 = 2
            <AttrsOfEnumWithCustomAttr(-3, "this is three")>
            Member3 = 3
        End Enum
    
        <AttributeUsage(AttributeTargets.Field)>
        Public Class AttrsOfEnumWithCustomAttr : Inherits System.Attribute
            Public SomeValue As String
            Public SomeNum As Integer
            Public Sub New(num As Integer, Valu As String)
                Me.SomeNum = num
                Me.SomeValue = Valu
            End Sub
        End Class
    
        Public Shared Function MyEnumCustomAttr(ByVal EnumConstant As EnumWithCustomAttr) As AttrsOfEnumWithCustomAttr
            Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
            Dim sa As AttrsOfEnumWithCustomAttr = DirectCast(fi.GetCustomAttributes(GetType(AttrsOfEnumWithCustomAttr), True)(0), AttrsOfEnumWithCustomAttr)
            Return sa
        End Function
    
    
    '  TO USE
    
            Dim bar As EnumWithCustomAttr = EnumWithCustomAttr.Member3
            Dim a As AttrsOfEnumWithCustomAttr = MyEnumCustomAttr(bar)
    

    Hope it helps.


    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it."

    - from former MSDN User JohnWein

    SerialPort Info

    Multics - An OS ahead of its time.


    • Edited by dbasnett Tuesday, December 18, 2018 5:50 PM
    • Marked as answer by Dick Watson Tuesday, December 18, 2018 6:22 PM
    Tuesday, December 18, 2018 2:13 PM
  • I'm not sure I follow everything you are doing, but having the base class create an instance of the derived class (Public Shared t As New OpResultsTxt) is not really workable since I want to implement multiple of the derived classes for different enumerations. This was why I was hoping the MustOverride/Overrides of LoadDict() would enable the derived class LoadDict() methods to serve as the derived-class-specific methods for the base class to load their dictionary entries for them.

    Taking off from your approach, though, I tried to see if there was a way for the derived object to associate itself with the t object in the base class.

        Public Class EnumTxt
    
            ' changed:
            Public Shared t As Object
            Const NoEnumTxt = "(no text available)"
    
            Public Shared ReadOnly Property Txt(key As [Enum]) As String
                Get
                    If myDict Is Nothing Then t.LoadDict()
                    If myDict.ContainsKey(key) Then
                        Txt = myDict(key)
                    Else
                        Txt = NoEnumTxt
                    End If
                End Get
            End Property
    
            Public Overridable Sub LoadDict()
    
            End Sub
    
        End Class
    
        Public Class OpResultsTxt
            Inherits EnumTxt
            Overrides Sub LoadDict()
                ' added:
                t = New OpResultsTxt
                myDict = New Dictionary(Of [Enum], String) From {
                {OpResults.NoSourceFile, "No source file associated with this workbook"},
                {OpResults.SheetNotWorksheet, "Current sheet not worksheet"},
                {OpResults.SelectionNotRange, "Current selection not a range"}
            }
            End Sub
        End Class

    But, again, chicken and egg. I also tried assigning the derived object to t in a t.New() method in the derived class, but, obviously, if never instantiated, it's never run.

    Tuesday, December 18, 2018 5:44 PM
  • This approach is eye-opening to yet another layer of Visual Studio magic tricks, but is maybe not so good at separating the strings, which might be localized at some future point, from the more breakable enumerations.

    I may just end up having to instantiate an object to do this somehow and bag all this Shared stuff.

    Tuesday, December 18, 2018 5:49 PM
  • Can you describe what you are trying to do without how you are trying to accomplish it please.

    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it."

    - from former MSDN User JohnWein

    SerialPort Info

    Multics - An OS ahead of its time.

    Tuesday, December 18, 2018 5:52 PM
  • Upon further consideration, I'm rapidly growing to like this. The Enums I want to use it for mostly, if not exclusively, exist for the sake of the association with the string. So maybe having the Enums exist only with the strings is, net, a plus anyway.
    Tuesday, December 18, 2018 6:23 PM
  • Create what are effectively arrays of constant strings--tied to related values of a given Enum. I want to isolate the strings from code as much as possible, make them as easy to reference as a constant, and tie them to the Enums, in case the Enums change.
    Tuesday, December 18, 2018 6:55 PM