vb.net - 为什么从接口(interface)值设置对象仅在一行 if 语句上抛出 invalidCastException

标签 vb.net interface

使用包含接口(interface)的 if 语句设置数组对象时,我抛出 InvalidCastException,需要了解原因

我知道如何解决实际问题,我只是很困惑为什么会发生这种情况。

请注意,customerOne 和 Two 是接口(interface)(即 ICustomer),customers 是“Customer”数组(不是接口(interface))

只有当它是数组时才会出错,如果我尝试将单个对象设置为接口(interface)值,它会正常工作

Dim customerOne as ICustomer
Dim customerTwo as ICustomer

---^^ 这些被填充...然后我们创建一个数组:

Dim customers as Customer()

现在,奇怪的部分......

customers = {customerOne, customerTwo}

以上效果很好

customers = If(myBool, {customerOne,customerTwo}, {customerOne,customerTwo})

上述失败并出现 InvalidCastException

我希望 If 语句返回与工作示例完全相同的值,因此不会抛出 InvalidCastException

有人了解这种行为的原因吗?

最佳答案

你的代码之所以能够编译,是因为你有Option Strict Off,这使得某些错误在编译时被隐藏在雷达之下,只能在运行时通过异常发现。如果您打开Option Strict On(我和大多数其他人都建议您这样做),则该行无法编译:

customers = {customerOne, customerTwo}

它给出的错误是:

BC30512 Option Strict On disallows implicit conversions from 'ICustomer' to 'Customer'

原因是表达式 {customerOne, customerTwo} 计算结果为 ICustomer 对象数组,因为这是变量的类型。在编译时,编译器无法知道这两个变量一定会引用 Customer 对象,因为理论上它们可以引用恰好实现该接口(interface)的任何类型的对象。因此,它能做的最好的事情就是根据初始化器中给定的变量确定数组的类型。

因此,该表达式的计算结果为 ICustomer() 数组,但您尝试为其分配的变量是 Customer() 数组。由于 CustomerICustomer 更具体,因此不会自动允许该分配。为了让它编译,你必须显式地转换它。您可以转换数组初始值设定项中的项目,以使其计算为正确的数组类型:

customers = {DirectCast(customerOne, Customer), DirectCast(customerTwo, Customer)}

或者,使用一点 LINQ,您可以让它计算出错误类型的数组,然后强制转换整个数组:

customers = {customerOne, customerTwo}.Cast(Of Customer)().ToArray()

但是,这两种选择都不安全。它们都允许在运行时发生类型检查异常。因此,如果可能的话,最好以允许编译器在编译时安全地进行所有类型检查的方式重写代码。例如,如果将 customers 变量更改为 ICustomer 数组,而不是具体 Customer 类型的数组,那么它将起作用很好:

Option Strict On

Public Module MyModule
    Public Sub Main()
        Dim myBool As Boolean = False
        Dim customerOne As ICustomer = New Customer()
        Dim customerTwo As ICustomer = New Customer()
        Dim customers As ICustomer()

        ' Both of these lines compile fine because the arrays are all ICustomer()
        customers = {customerOne, customerTwo}
        customers = If(myBool, {customerOne, customerTwo}, {customerOne, customerTwo})
    End Sub

    Public Class Customer
        Implements ICustomer
    End Class

    Public Interface ICustomer
    End Interface
End Module

至于三元 If 运算符将其丢弃的原因......好吧,这更复杂并且超出了我的理解范围。我尽量避免使用Option Strict Off,所以它到底是如何工作的不是我的专业知识。然而,从高层次来看,所发生的情况是 If 运算符在中间添加了额外的求值和类型推断层,这阻碍了 VB 为您执行自动类型转换的尝试。例如,如果您使用 Option Strict Off 执行此操作,它会起作用:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
customers = {customerOne, customerTwo}

如果您这样做,您将看到 {customerOne, customerTwo} 的计算结果实际上是一个 ICustomer() 数组:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
Dim temp As Object = {customerOne, customerTwo}
Console.WriteLine(temp.GetType().Name)

但是,如果这样做,它将在运行时抛出异常:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
Dim temp As Object = {customerOne, customerTwo}
customers = temp

它抛出的异常是:

System.InvalidCastException: Unable to cast object of type 'ICustomer[]' to type 'Customer[]'

仅仅将其分为两个步骤,强制它首先评估输入数组,就会失败。仅当您创建数组并在同一命令中分配变量时它才有效。事实上,即使您在该中间变量上指定了正确的类型,它也会失败并出现相同的异常:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
Dim temp As ICustomer() = {customerOne, customerTwo}
customers = temp

这本质上就是 If 运算符所做的事情。它将其分为两个步骤,其中 If 运算符必须首先评估其操作数,以确定其本身被评估为哪种类型(即,如果它是方法,则返回什么)。中间的额外步骤使 VB 无法自动执行类型转换。具体原因我也说不上来。

关于vb.net - 为什么从接口(interface)值设置对象仅在一行 if 语句上抛出 invalidCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55968504/

相关文章:

vb.net - C1FlexGrid 从单元格复选框中获取值

vb.net - DateTime.ToLocalTime() 于 2013 年 8 月停止在 XP 上工作

interface - "Code in Interfaces"Kotlin,他们如何避免 "deadly diamond of death"?

python - 在 Python 中实现类似接口(interface)的好方法是什么?

vb.net - 如何使用 vb.net 以编程方式对 Excel 中的列进行排序?

c# - 是否存在任何NON-GPL/AGPL病毒扫描库?

vb.net - 在 .NET 中调用 SmtpClient.Dispose() 是否安全?

Java : A List of type interface

ios - 是否有在没有不可访问的 Interface Builder 的情况下编写 GUI 的教程?

enums - 在 Kotlin 中,当枚举类实现接口(interface)时,如何解决继承的声明冲突?