0

Object の拡張メソッドは Objectで宣言できますが、 のように使用することはできませんobj.ExtMethod()。これは仕様によるものです。一方、 のように任意の拡張メソッドを使用することもできますExtMethod(obj)Objectで宣言された拡張メソッドの呼び出しが、他の型で宣言された拡張メソッドと異なるのはなぜですか? この背後にあるロジックを探しています。それともバグですか?

違いを見つけるには、以下の例を参照して、通常ToString1()ToString2()/を比較してくださいToString3()

Imports System.Runtime.CompilerServices

Module CompilerExtensionsModule

    ' standard one, works as expected
    <Extension>
    Function ToString1(value As Integer) As String
        Return value.ToString()
    End Function

    ' obj isn't expected as parameter on actual usage, context is supplied instead
    <Extension>
    Function ToString2(obj As Object) As String
        Return If(obj Is Nothing, "", obj.ToString())
    End Function

    ' this is way how to have obj as parameter - first parameter is ignored
    <Extension>
    Function ToString3(base As Object, obj As Object) As String
        Return If(obj Is Nothing, "", obj.ToString())
    End Function

    ' let's try with something different than Object
    <Extension>
    Function ToStringClass1(obj As Class1) As String
        Return obj.ToString()
    End Function

End Module

クラスでの使用法:

ToString1(3)    ' as expected - 1 parameter declared, 1 expected
ToString2()     ' 1 parameter declared, no parameters expected in call
ToString3(Nothing) ' 2 parameters declared, 1 expected in call - passed as second parameter

追加された詳細: (最小限の完全な動作例 – 3 つのファイル – 上記のファイルを含む)

完全な呼び出しコンテキスト:クラス:

Public Class Class1

    Sub Action1()
        Dim value1 As Integer = 1
        Dim obj1 As Object = Nothing
        Dim obj2 As New Class1

        Console.WriteLine(ToString1(value1))
        Console.WriteLine(ToString2())
        Console.WriteLine(ToString3(obj1))
        Console.WriteLine(ToStringClass1())
    End Sub

End Class

完全な呼び出しコンテキスト: Class1とは異なるクラス– 問題は投稿されていませんが、奇妙な効果:

Public Class Class2

    Sub Action1()
        Dim value1 As Integer = 1
        Dim obj1 As Object = Nothing
        Dim obj2 As New Class1

        Console.WriteLine(ToString1(value1))
        Console.WriteLine(ToString2())
        Console.WriteLine(ToString3(obj1))
        Console.WriteLine(ToStringClass1(obj2))

        obj2.ToString2()
        ToString2(obj2) ' INVALID - won't compile in any class (but will do in any module)
        ToString3(obj2) ' EDIT: VALID because two parameters are actually supplied here

        ' EDIT (see comments below the answer):
        CompilerExtensionsModule.ToString2(obj2) ' VALID - switching the context solves it
        ' Note: for ext.mehods of Object, this form of call is needed in any class
        ' Reason: any class is descendant of Object => VB wants to supply 1st parameter
        '    in calling context of class => use calling context of ext.module instead

    End Sub

End Class

完全な呼び出しコンテキスト:モジュール– 問題なし:

Module Module1

    Sub Main()
        Dim value1 As Integer = 1
        Dim obj1 As Object = Nothing
        Dim obj2 As New Class1

        Console.WriteLine(ToString1(value1))
        Console.WriteLine(ToString2(obj1))
        Console.WriteLine(ToString3(obj1, obj1))
        Console.WriteLine(ToStringClass1(obj2))

        ' unlike in Class2, no issues here:
        obj2.ToString2()
        ToString2(obj2)

    End Sub

End Module
4

1 に答える 1

3

Option Strict On が存在する場合、オブジェクトで宣言された拡張メソッドの呼び出しが他の型で宣言された拡張メソッドと異なるのはなぜですか?

呼び出しコンテキスト (表示されていません) は に変換できませんIntegerが、 に変換できるためObjectです。明示的に呼び出していると想像してください:

Me.ToString2()
Me.ToString3(Nothing)

それは次のように変換されます:

ToString2(Me)
ToString3(Me, Nothing)

に暗黙的に変換できないため、(通常の共有モジュール全体のメソッドとして扱われる)では発生しません。(VB でのメソッド呼び出しの詳細はわかりませんが、モジュール全体の共有メソッドが通常の方法で呼び出される前に、拡張メソッドが検索されるようです。)ToString1MeInteger

于 2015-09-08T16:35:57.493 に答える