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?
精彩评论