Search code examples
.netvb.netgenericsnullablectype

With CType Int32? to Int64? - System.InvalidCastException: Specified cast is not valid


Why is CType complaining (InvalidCastException) about taking an object (that is really an Int32?) and converting it to an Int64??

I've found that CTypeDynamic isn't having an issue (tangential though, as I'm focused on Ctype).

Here is a code sample to reproduce the scenario.

Module Module1

  Sub Main()

    Dim i As Int32? = 1234567891

    'manual nullable -> non nullable -> non nullable -> nullable
    'per https://stackoverflow.com/a/10065482/392175
    Dim iNotNullable As Int32 = i.Value
    Dim biNotNullable As Int64 = iNotNullable
    Dim bi As Int64? = biNotNullable

    Console.WriteLine($"---Manual results---")
    Console.WriteLine($"i={i}")
    Console.WriteLine($"bi={bi}")

    'CType investigation
    bi = Module1.xCType(Of Int64?)(i)

    Console.WriteLine($"---CType results---")
    Console.WriteLine($"i={i}")
    Console.WriteLine($"bi={bi}")

    Console.ReadLine()
  End Sub

  Public Function [xCType](Of T)(ByVal obj As Object) As T
    If obj Is Nothing Then Return Nothing
    If IsDBNull(obj) Then Return Nothing

    Return obj  'fails

    Return CType(obj, T)  'fails

    Return CTypeDynamic(Of T)(obj)  'succeeds
  End Function

End Module

Solution

  • You have two problems, both related to insufficient type information available at compile time.

    1. .NET doesn't specialize generic methods. One compilation, based on the constraints, has to work for every run-time value of the generic parameter.

      At the time the compiler sees xCType it doesn't know that T is a nullable type, so it can't choose the rule for nullable cast (S? to S to T to T?) and even if you constrain to a generic nullable the middle conversion (S to T) will still fail because that's type-specific not generic.

    2. obj has static type Object, so again the compiler doesn't know that the actual value passed in will be a nullable and cannot select the nullable conversion sequence. And again the middle conversion in the sequence (S to T) cannot be found when the compile-time type information is missing.

    CTypeDynamic overcomes both these problems, by looking at the runtime type instead of the compile-time type.