3

遇到了一些我觉得有趣的东西,并希望得到解释。

编辑

这个问题并不是要回答应该采取什么措施来解决它。我知道修复。我想要解释为什么编译器会这样做。前任。在这种情况下是否不考虑私有功能?

问题

我有一个类,它有一个名为 WhatIs 的公共共享(静态)函数。WhatIs 接受一个包含对象集合的参数。代码遍历这个集合并调用一个 WhatIs 函数,该函数的参数类型与对象是什么相匹配。

执行时,会引发 InvalidCastException 异常,因为执行尝试调用启动此的 WhatIs 函数,而不是所提供类型的函数。

这很奇怪,但让我感到奇怪的是,当您将私有共享功能更改为公共共享时,它工作正常。

更奇怪的是,当您显式转换对象时,即使该函数是私有的,它也可以工作。

什么?!有人请解释

代码

胆量:

Public Class House
    Public Property Furniture As ICollection(Of Object)

    Public Sub New()
        Furniture = New List(Of Object)
    End Sub
End Class

Public Class Chair
    Public Property IsComfortable As Boolean
End Class

Public Class Table
    Public Seats As Integer
End Class

Public Class HouseExaminer
    Public Shared Function WhatIs(thing As House) As String
        Dim isA As String = "a house that contains "

        For Each item In thing.Furniture
            isA &= WhatIs(item)
        Next

        Return isA
    End Function

    Private Shared Function WhatIs(thing As Chair) As String
        Return "a " & If(thing.IsComfortable, "comfortable", "uncomfortable") & " chair "
    End Function

    Private Shared Function WhatIs(thing As Table) As String
        Return "a table that seats " & thing.Seats & " iguanas"
    End Function
End Class

去测试

Imports System.Text
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports stuff

<TestClass()>
Public Class HouseExaminerTests

    <TestMethod()>
    Public Sub TestWhatIs()
        Dim given As New House()
        Dim expected As String
        Dim actual As String

        given.Furniture.Add(New Chair() With {.IsComfortable = True})
        given.Furniture.Add(New Table() With {.Seats = 4})

        expected = "a house that contains a comfortable chair a table that seats 4 iguanas"
        actual = HouseExaminer.WhatIs(given)

        Assert.Equals(expected, actual)
    End Sub
End Class

结果

调试测试,你会得到这个: InvalidCastException Method invocation failed because 'Public Shared Function WhatIs(thing As stuff.House) As String' 不能用这些参数调用:

参数匹配参数 'thing' 无法从 'Chair' 转换为 'House'。

这些变化使它工作,但为什么呢?!

公开他们

将 HouseExaminer 中的私有共享函数更改为 public,重新运行测试。扰流板,它有效

显式转换对象

将它们改回私有然后替换

isA &= WhatIs(item)

If TypeOf item Is Chair Then isA &= WhatIs(CType(item, Chair))
If TypeOf item Is Table Then isA &= WhatIs(CType(item, Table))

重新运行测试,你知道什么,它有效

4

2 回答 2

4

首先,您似乎打开了隐式转换。这就是问题的开始。其次,您定义FurnitureList(of Object). 你的第一个电话WhatIs是成功的。编译器在传递它所看到的内容时对使用哪个重载进行了最佳猜测,Object因为它在迭代时很简单thing.Furniture,并且它确定该WhatIs方法的公共静态版本是最合适的。然后它尝试隐式转换ObjectHouse,并且不可避免地失败。

为什么铸造有效?因为它在确定使用哪个重载时需要猜测。

故事的寓意是:不要让编译器猜测。隐式转换会导致棘手的错误。

编辑为什么编译器看不到其他重载函数?

编译器必须确定要在编译时使用的正确重载。它不会等到运行时才确定要使用哪个重载,因此没有检查对象类型以确定最合适的重载的奢侈。

由于编译器只知道它furniture是 a List(Of Object),因此从技术上讲(启用隐式转换)所有三个重载都被认为是“适当的”,但编译器必须选择一个。它对可能的过载候选者进行排名,并选择优先public于那些的版本private

于 2013-04-10T17:26:20.300 回答
2
  1. 始终使用

    选项严格开启

  2. 您不能通过添加名称相同的方法来使其更灵活,只是使用不同的参数类型。

更新

Private Function ShowMe(data As Integer) As String
    Return data.ToString
End Function

Private Function ShowMe(data As String) As String
    Return data
End Function

Private Function ShowMe(data As Double) As String
    Return data.ToString
End Function

Dim bla As New List(Of Object)

如果你再打电话

    bla.Add(12)
    bla.Add("hi")
    bla.Add(1.2)
    Dim text As String
    text = ShowMe(bla(0))
    text = ShowMe(bla(1))
    text = ShowMe(bla(2))

那么编译器总是会抱怨不存在正确的方法,因为正确的方法不是通过检查类型来选择的,而是通过定义选择的,容器是为哪种类型定义的。

Private Function ShowMe(data As Object) As String
    Return data.ToString
End Function

这将对所有整数、双精度数和字符串调用。如果它不可用,则使用一些可以进行某种自动转换的方法。这就是为什么您可以将整数放入浮点数或将数字放入字符串的原因。

一种方法是检查其类型并进行显式类型转换

    For Each ele As Object In bla
        If TypeOf ele Is Integer Then
            text = ShowMe(CInt(ele))
        ElseIf TypeOf ele Is Double Then
            text = ShowMe(CDbl(ele))
        Else
            text = ShowMe(CStr(ele))
        End If
    Next

但这仍然不是那么干净。如果你想访问所有对象都应该支持的属性,那么将它们放在一个容器中并将类型定义为确保这些属性存在的东西。

于 2013-04-10T17:32:15.537 回答