I have the following Function (try-catch removed):
Friend Shared Function ConvertOrDefault(Of T As {Structure, IConvertible})(convertFrom As Object, ignoreCase As Boolean) As T
Dim retVal As T
If Not GetType(T).IsEnum Then
Throw New ArgumentException("Type must be enum")
ElseIf convertFrom Is Nothing OrElse Not TypeOf convertFrom Is String Then
Return New T
ElseIf [Enum].TryParse(convertFrom.ToString(), ignoreCase, retVal) Then
Return retVal
Else
Return New T
End If
End Function
Which converts the given type to an enum (hence the constraints), if it is one.
That's fine, but I then have another method (simplified below) that does more general casting, and I want it to use that method if the type passed in is an enum:
Friend Shared Function Convert(Of T)(value as Object) As T
If GetType(T).IsEnum Then
Return Enums.ConvertOrDefault(Of T)(value, True)
Else : return DirectCast(value, T)
End If
End Function
For the call to Enums.ConvertOrDefault, this gives the errors:
Type argument 'T' does not inherit from or implement the constraint type 'System.IConvertible' Type argument 'T' does not satisfy the 'Structure' constraint for type parameter 'T'
How can I say "it's OK, I know it's an Enum so it's fine"?
--- Edit ---
One (very ugly) way to do it is as follows:
Dim type As Type = GetType(T)
If type.IsEnum Then
Select Case type.Name
Case "EnumTypeOne"
Return DirectCast(DirectCast(Enums.ConvertOrDefault(Of EnumTypeOne)(value, True), Object), T)
' ...
But that's hideous. Surely there's a way to generalise that?
-- Edit 2: Purpose --
I'm reading data from an Oracle database, which stores the Enums (of which I have several) as strings; as well as storing other data in various formats (Byte() as RAW, TimeSpan as IntervalDS, etc). I then use the Convert function as a generic function where, given the result of datareader(column), I can convert that object into the appropriate type.
All of the Oracle.DataAccess.Client.OracleDataReader's .Get... functions take an index rather than a column name; and as I can't guarantee the order of the columns, and for the sake of readability, using the column name makes more sense - but then I have to parse the output myself.
So my code is doing something like:
Dim id as Byte() = Convert(dataReader("id_column"))
Dim something as SomeEnum = Convert(dataReader("somethingCol"))
'...
I could deliberately call Enum.ConvertOrDefault instead of Convert when I'm expecting an Enum, but that seems to break the principle of a general method, which I think makes more sense... and would also allow me to reuse that method in other contexts.
Hope that helps clarify a bit.
--- Edit 3 ---
I tried this idea, from the comments:
Friend Shared Function Convert(Of T As {New})(value as Object) as T
and
Friend Shared Function ConvertOrDefault(Of T As{New}) convertFrom As Object, ignoreCase As Boolean) As T
If Not GetType(T).IsEnum Then
Throw New ArgumentException("Type must be enum")
ElseIf convertFrom Is Nothing OrElse Not TypeOf convertFrom Is String Then
Return New T
End If
Try
Return CType([Enum].Parse(GetType(T), convertFrom.ToString(), ignoreCase), T)
Catch ex As Exception
End Try
' default
Return New T
End Function
But this gives errors when I call the Convert method for types like String or Byte(), saying
"Type argument 'String' must have a public parameterless instance constructor to satisfy the 'New' constraint for type parameter 'T'
Enumto the constraints but then force an error in the instantiation. This does do something very similar to a constraints failure.Enumwhere?