开发者

Is there a way to mimic an interface/contract for shared methods in a class for use by generics?

So I implemented my own form of Enum static classes so I can associate strings to enumeration values. One of the shared functions in each enum class, GetValue, is used to quickly look up the string value from an internal array via the offset. While all of the enum classes implement the same exact function, the data types of their parameters differ, specific to the enums in each class.

So I come to a point where I want to create a generic class that can use any of the enums by passing it as a generic type parameter. But I cannot find a way to constrain type T in such a way to be able to call each enum's GetValue function. This is where an interface would come in handy, but VB.NET forbids interfaces for shared methods.

I have a base class that I could constrain to, but the base class doesn't implement the GetValue method, and I can't define a generic version of it, because I need to rely on shared properties of each child class in order for GetValue to do its thing.

Is there another way to tackle this?

EDIT:

Here's what an example enum class looks like. The parent, EBase is nothing special, just hosting common Name, Value, and Type properties, plus two shared operators, op_Equality and op_Inequality. Imagine using a couple of these enum classes with a generic method (or class) and needing to access the GetValue member via the generic type parameter T.

Friend NotInheritable Class EExample
    Inherits EBase

    Private Sub New()
    End Sub


    Friend Shared Function GetValue(ByVal Name A开发者_如何转开发s String) As Enums
        Return Enums(Array.IndexOf(_Names, Name))
    End Function


    'Num of Enums defined.
    Friend Shared ReadOnly MaxEnums As Int32 = 5

    'String literals.
    Private Shared ReadOnly _Names As String() = New String() _
        {"one_adam", "two_boy", "three_charles", "four_david", "five_edward"}

    'Enums.
    Friend Shared ReadOnly OneA As New Enums(_Names(0), 0)
    Friend Shared ReadOnly TwoB As New Enums(_Names(1), 1)
    Friend Shared ReadOnly ThreeC As New Enums(_Names(2), 2)
    Friend Shared ReadOnly FourD As New Enums(_Names(3), 3)
    Friend Shared ReadOnly FiveE As New Enums(_Names(4), 4)


    'Enum Values Array.
    Friend Shared ReadOnly Values As Enums() = New Enums() _
        {OneA, TwoB, ThreeC, FourD, FiveE}


    Friend NotInheritable Class Enums
        Inherits EBase

        Private Sub New()
        End Sub

        Friend Sub New(ByVal Name As String, ByVal Value As Int32)
            MyBase.Name = Name
            MyBase.Value = Value
            MyBase.Type = GetType(Enums)
        End Sub
    End Class
End Class

EDIT2: Here's how the things are used:

Dim Foo As EExample.Enums
Foo = EExample.TwoB
Debug.Print(Foo.Name)

will print two_boy


How about this as a solution:

Friend NotInheritable Class EExample
    Inherits EBase

    Protected Sub New()
    End Sub

    Protected Overrides Function BuildValues() As EBase.Enums()
        Return New Enums() _
        { _
            Build(Of EExample)("one_adam", 0), _
            Build(Of EExample)("two_boy", 1), _
            Build(Of EExample)("three_charles", 2), _
            Build(Of EExample)("four_david", 3), _
            Build(Of EExample)("five_edward", 4) _
        }
    End Function

    Private Shared _instance As EExample = New EExample()

    Friend Shared ReadOnly OneA As Enums = _instance.Values(0)
    Friend Shared ReadOnly TwoB As Enums = _instance.Values(1)
    Friend Shared ReadOnly ThreeC As Enums = _instance.Values(2)
    Friend Shared ReadOnly FourD As Enums = _instance.Values(3)
    Friend Shared ReadOnly FiveE As Enums = _instance.Values(4)

End Class

EBase then looks like this:

Public MustInherit Class EBase

    Protected MustOverride Function BuildValues() As Enums()

    Private _values As EBase.Enums()

    Friend ReadOnly Property Values() As EBase.Enums()
        Get
            If _values Is Nothing Then
                _values = Me.BuildValues()
            End If
            Return _values
        End Get
    End Property

    Friend ReadOnly MaxAlterEnums As Int32 = Me.Values.Length

    Friend Function GetValue(ByVal Name As String) As Enums
        Return Me.Values.Where(Function(n) n.Name = Name).FirstOrDefault()
    End Function

    Friend Shared Function Build(Of T As EBase)(ByVal name As String, ByVal value As Int32) As Enums
        Return New Enums(name, value, GetType(T))
    End Function

    Public Class Enums
        Public Sub New(ByVal name As String, ByVal value As Int32, ByVal type As Type)
            Me.Name = name
            Me.Value = value
            Me.Type = type
        End Sub
        Private _name As String
        Public Property Name() As String
            Get
                Return _name
            End Get
            Set(ByVal Value As String)
                _name = Value
            End Set
        End Property
        Private _value As Integer
        Public Property Value() As Integer
            Get
                Return _value
            End Get
            Set(ByVal Value As Integer)
                _value = Value
            End Set
        End Property
        Private _type As Type
        Public Property Type() As Type
            Get
                Return _type
            End Get
            Set(ByVal Value As Type)
                _type = Value
            End Set
        End Property
    End Class

End Class

Now your sample code works exactly as before:

    Dim Foo As EExample.Enums
    Foo = EExample.TwoB
    Debug.Print(Foo.Name)

And you are dealing with plain instances for you code implementation. The only Shared values are the five Enums and a private _instance variable.

You can now restrict on EBase.

Does this work for you?


Kumba pointed out that I didn't expose the GetValue function on the EExample class.

Just add this to EExample:

    Friend Shared Function GetValue(ByVal Name As String) As Enums
        Return _instance.GetValue(Name)
    End Function


Wow, this sure sounds like it's getting deep quick. Are you sure you need all that?

Wouldn't it be easier to associate the strings to enumeration values by creating a class, having the enums in the same file (or even nested in the same namespace, if you prefer), and then using a .ToFriendlyName() method to extract the string (that is, if it's designed for a human), and if you need to go the other way, you could create a constructor of the class that has a string argument and stores the associated enum.

Do you really need all of the arrays, inheritance and .GetValue overloads? Maybe I'm misunderstanding, but from your initial description it sounds like the solution might be over-engineered by quite a bit.

Can you provide a bit more detail, especially regarding the "enum static classes" that you started from? What's the root problem you're trying to solve?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜