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'