3

面白いと思ったものに出くわし、説明が欲しい.

編集

この質問は、それを修正するために何をすべきかについて回答することを意図したものではありません。私は修正を知っています。コンパイラがなぜそれを行うのかについて説明が必要です。元。このシナリオでは、プライベート関数は考慮されませんか?

問題

WhatIs というパブリック共有 (静的) 関数を持つクラスがあります。WhatIs は、オブジェクトのコレクションを持つパラメーターを受け取ります。コードはこのコレクションを繰り返し処理し、オブジェクトの型に一致するパラメーターを持つ WhatIs 関数を呼び出します。

実行すると、InvalidCastException 例外がスローされます。これは、実行が、提供された型の WhatIs 関数ではなく、これを開始した 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 メソッドの呼び出しが失敗しました。これは、'Public Shared Function WhatIs(thing As stuff.House) As String' をこれらの引数で呼び出すことができないためです:

引数一致パラメーター 'thing' は 'Chair' から 'House' に変換できません。

これらの変更により機能しますが、なぜですか?!

それらを公開する

HouseExaminer のプライベート共有関数をパブリックに変更し、テストを再実行します。スポイラー、それは動作します

オブジェクトを明示的にキャストする

それらをプライベートに戻してから交換します

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

まず、暗黙的な変換がオンになっているようです。それが問題の始まりです。次に、 として定義FurnitureしますList(of Object)。への最初の呼び出しWhatIsは成功しています。コンパイラは、単純に を反復処理するのと同じように、参照するもの渡すときにどのオーバーロードを使用するかについて最善の推測を行い、メソッドの public static バージョンが最も適切であると判断します。次に、暗黙的に への変換を試み、必然的に失敗します。 Objectthing.FurnitureWhatIsObjectHouse

キャストはなぜ機能するのですか?どのオーバーロードを使用するかを決定する際に推測作業が必要になるためです。

話の教訓は次のとおりです。コンパイラに推測させないでください。暗黙的な変換は、厄介なバグにつながる可能性があります。

編集: コンパイラが他のオーバーロードされた関数を認識しないのはなぜですか?

コンパイラは、コンパイル時に使用する正しいオーバーロードを決定する必要があります。使用するオーバーロードを決定するために実行時まで待たないため、オブジェクトの型を検査して最も適切なオーバーロードを決定する余裕はありません。

コンパイラはそれが であることしか認識していないためfurniture(List(Of Object)暗黙的な変換がオンになっている場合)、3 つのオーバーロードはすべて「適切」と見なされますが、コンパイラは 1 つを選択する必要があります。過負荷の可能性のある候補をランク付けし、それら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

これは、すべての整数、倍精度、および文字列に対して呼び出されます。利用できない場合は、ある種の自動変換を実行できるいくつかのメソッドが使用されます。これが、浮動小数点数に整数を入れたり、文字列に数値を入れたりできる理由です。

1 つの方法は、その型を確認し、explizit 型変換を行うことです。

    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 に答える