CFP CFP - 2 years ago 149 Question

Typecasting generic parameters

Using the following code:

Function GetSetting(Of T)(ByVal SettingName As String, ByRef DefaultVal As T) As T
Return If(Configuration.ContainsKey(SettingName), CType(Configuration(SettingName), T), DefaultVal)
End Function

Yields the following error:

Value of type 'String' cannot be converted to 'T'.

Any way I could specify that in all cases, the conversion will indeed be possible (I'm basically getting integers, booleans, doubles and strings).

Edit: There seem to be three solutions now:

  • Using the `ValueAs` function provided by AMissico

  • Casting to an `object`, then to `T`, with a check for null values

  • Using a `DirectCast` over Convert.ChangeType

Which would you suggest?

Edit 2:
Would this code work?

Function GetSetting(Of T)(ByVal SettingName As String, Optional ByRef DefaultVal As T = Nothing) As T
Return If(Configuration.ContainsKey(SettingName), ConvertTo(Of T)(Configuration(SettingName)), DefaultVal)
End Function

Function ConvertTo(Of T)(ByVal Str As String) As T
Return If(Str Is Nothing Or Str = "", Nothing, CType(CObj(Str), T))
End Function

Edit 3: [AMJ] Working Code

Function GetSetting(Of T)(ByVal SettingName As String) As T
Return GetSetting(Of T)(SettingName, Nothing)
End Function
Function GetSetting(Of T)(ByVal SettingName As String, ByVal DefaultVal As T) As T
Dim sValue As String = Configuration(SettingName)
If Len(sValue) = 0 Then
Return DefaultVal
Return CType(CObj(sValue), T)
End If
End Function

Quick Test Method

Public Sub DoIt()

Me.Configuration.Add("KeyN", Nothing)
Me.Configuration.Add("KeyE", String.Empty) '""
Me.Configuration.Add("Key1", "99")
Me.Configuration.Add("Key2", "1/1/2000")
Me.Configuration.Add("Key3", "True")
Me.Configuration.Add("Key4", "0")

Dim o As Object 'using object in order to see what type is returned by methods

o = Value(Of Integer)("KeyN", 10) '10
o = Value(Of Integer)("KeyE", 10) '10
o = Value(Of Integer)("Key1", 10) '99

o = Value(Of Date)("KeyN", #11/11/2010#)
o = Value(Of Date)("KeyE", #11/11/2010#)
o = Value(Of Date)("Key2", #11/11/2010#)

o = GetSetting(Of Integer)("KeyN", 10) '10
o = GetSetting(Of Integer)("KeyE", 10) '10
o = GetSetting(Of Integer)("Key1", 10) '99

o = GetSetting(Of Date)("KeyN", #11/11/2010#)
o = GetSetting(Of Date)("KeyE", #11/11/2010#)
o = GetSetting(Of Date)("Key2", #11/11/2010#)

End Sub

Answer Source

The Value(Of T) and ValueAs methods support nullable-types. I used Microsoft .NET 2.0 source code as a reference.

This is well-tested and production ready code.

There is no error handling in these "library" functions. It is the responsibility of the caller to handle any conversion errors that occur. The only conversion errors generated are obvious errors, such as trying to convert the string "abc" to Integer.

Public Sub DoIt()
    Dim o As Object
    o = Value(Of Integer)("foo", 10)
    o = Value(Of DateTime)("xxx", #1/1/2000#)
    o = Value(Of Boolean?)("nop", True)
End Sub

Public Function GatherTag(ByVal tag As String) As String
    If tag = "foo" Then
        Return "99"
        Return String.Empty
    End If
End Function

''' <summary>
''' Provides strongly-typed access to the tag values. The method also supports nullable types.
''' </summary>
''' <typeparam name="T">A generic parameter that specifies the return type.</typeparam>
''' <param name="tag">The ExifTool Tag Name,</param>
''' <returns>The value, of type T, of the tag.</returns>
Public Function Value(Of T)(ByVal tag As String, ByVal defaultValue As T) As T
    Return DirectCast(ValueAs(GetType(T), tag, defaultValue), T)
End Function

''' <summary>
''' Returns the tag's value as the specified type. The method also supports nullable types.
''' </summary>
''' <param name="type">The type to return the tag value as.</param>
''' <param name="tag">The ExifTool Tag Name,</param>
''' <returns>The value of the tag as the type requested.</returns>
Public Function ValueAs(ByVal type As System.Type, ByVal tag As String, ByVal defaultValue As Object) As Object
    Dim oResult As Object = Nothing

    Dim oTag As String = GatherTag(tag)

    If Len(oTag) = 0 Then

        'use specified default value

        oResult = defaultValue


        'is requested type a generic type?

        If type.IsGenericType AndAlso type.GetGenericTypeDefinition Is GetType(Nullable(Of )) Then

            Dim oUnderlyingType As Type = Nullable.GetUnderlyingType(type)

            Dim oConstructed As Type = type.GetGenericTypeDefinition.MakeGenericType(oUnderlyingType)

            Dim oValue As Object

            oValue = System.Convert.ChangeType(oTag, oUnderlyingType)

            If oValue IsNot Nothing Then
                oResult = Activator.CreateInstance(oConstructed, oValue)
            End If


            'non-generic type

            oResult = System.Convert.ChangeType(oTag, type)

        End If

    End If

    Return oResult
End Function
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download