遇到了一些我觉得很有趣的事情,并希望得到解释。
修改的
这个问题并不是要回答应该采取什么措施来解决它。我知道修复。我想要解释为什么编译器会做它的功能。防爆。在这种情况下,是否考虑了私有函数?
问题
我有一个名为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的 方法调用失败,因为无法使用这些参数调用“公共共享函数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))
重新运行测试,你知道什么,它有效吗
答案 0 :(得分:4)
首先,看起来您已启用隐式转化。这是问题的开始。其次,您将Furniture
定义为List(of Object)
。您对WhatIs
的第一次呼叫正在成功。编译器在通过Object
迭代时传递它所看到的简单thing.Furniture
时,使用哪个重载进行最佳猜测,并确定公共静态版本WhatIs
方法是最合适的。然后它会尝试将Object
隐式转换为House
,并且不可避免地会失败。
为什么施法?因为确定使用哪个重载需要猜测。
故事的道德是:不要让编译器猜测。隐式转换可能会导致棘手的错误。
编辑:为什么编译器看不到其他重载函数?
编译器必须确定在编译时使用的正确重载。它不会等到运行时才能确定要使用哪个重载,因此无需检查对象的类型以确定最合适的重载。
由于编译器只知道furniture
是List(Of Object)
,从技术上讲(启用了隐式转换),所有三个重载都被认为是“合适的”,但编译器必须选择一个。它会对可能的重载候选进行排名,并在public
之前选择private
版本。
答案 1 :(得分:2)
使用始终
Option Strict On
通过添加名称相同的方法,只是使用不同的参数类型,无法使其更加灵活。
<强>更新强>
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
但这还不是那么干净。如果要访问所有对象应支持的属性,请将它们放在容器中,并将类型定义为确保存在这些属性的类型。