在 Object 上声明的扩展方法需要比其他方法多一个参数?

Extension methods declared on Object need one more parameter than others?

对象 can be declared on Object but cannot be used like 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

在class中的用法:

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 个文件 – 包括上面一个)

完整调用上下文:Class:

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

完整调用上下文: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

Why calling of extension methods declared on Object differ from extension methods declared on other types if Option Strict On is present?

因为您的调用上下文(您未显示)无法转换为 Integer,但可以转换为 Object。假设您正在明确调用:

Me.ToString2()
Me.ToString3(Nothing)

转换为:

ToString2(Me)
ToString3(Me, Nothing)

不会发生在 ToString1 中(它被视为常规的共享模块范围的方法),因为 Me 不是隐式可转换的至 Integer。 (我不知道 VB 中方法调用的细节,但听起来像是在以常规方式调用模块范围的共享方法之前搜索扩展方法。)