使用 CType Int32?到 Int64? - System.InvalidCastException: 指定的强制转换无效

问题描述

为什么 CType 会抱怨 (InvalidCastException) 获取一个对象(实际上是一个 Int32?)并将其转换为一个 Int64?

我发现 CTypeDynamic 没有问题(不过是无关紧要的,因为我专注于 Ctype)。

这是重现场景的代码示例。

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

解决方法

您有两个问题,都与编译时可用的类型信息不足有关。

  1. .NET 不专门用于泛型方法。基于约束的一次编译必须适用于泛型参数的每个运行时值。

    编译器在看到 xCType 时并不知道 T 是可空类型,因此无法选择可空类型转换的规则(S? 到 {{ 1}} 到 ST) 并且即使您限制为可空的通用中间转换(T?S)仍然会失败,因为这是特定于类型的而不是通用。

  2. T 具有静态类型 obj,因此编译器再次不知道传入的实际值将是可空值,因此无法选择可空值转换序列。当编译时类型信息丢失时,再次找不到序列中的中间转换(ObjectS)。

T 通过查看运行时类型而不是编译时类型来克服这两个问题。