问题描述
为什么 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
解决方法
您有两个问题,都与编译时可用的类型信息不足有关。
-
.NET 不专门用于泛型方法。基于约束的一次编译必须适用于泛型参数的每个运行时值。
编译器在看到
xCType
时并不知道T
是可空类型,因此无法选择可空类型转换的规则(S?
到 {{ 1}} 到S
到T
) 并且即使您限制为可空的通用中间转换(T?
到S
)仍然会失败,因为这是特定于类型的而不是通用。 -
T
具有静态类型obj
,因此编译器再次不知道传入的实际值将是可空值,因此无法选择可空值转换序列。当编译时类型信息丢失时,再次找不到序列中的中间转换(Object
到S
)。
T
通过查看运行时类型而不是编译时类型来克服这两个问题。