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

时间:2015-09-08 16:26:58

标签: vb.net call extension-methods roslyn option-strict

对象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

课堂上的用法:

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

1 个答案:

答案 0 :(得分:3)

  

如果存在Option Strict On,为什么调用在Object上声明的扩展方法与在其他类型上声明的扩展方法不同?

因为您的通话环境(您尚未显示)无法转换为Integer,但可转换为Object。想象一下你明确地打电话:

Me.ToString2()
Me.ToString3(Nothing)

转换为:

ToString2(Me)
ToString3(Me, Nothing)

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