Extension methods on Object can be declared on Object but cannot be used like obj.ExtMethod()
. This is by design. On the other hand, any extension method can be also used like ExtMethod(obj)
. Why calling of extension methods declared on Object differ from extension methods declared on other types? I'm seeking for logic behind this. Or is it a bug?
To spot the difference, please see below example and compare ordinary ToString1()
to 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
Usage in 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
Added details: (minimum complete working example – 3 files – including the above one)
Full calling context: 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
Full calling context: Class different from Class1 – no issues posted in question, but weird effects:
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
Full calling context: Module – no issues:
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?
Because your calling context (which you haven't shown) isn't convertible to Integer
, but is convertible to Object
. Imagine you're explicitly calling:
Me.ToString2()
Me.ToString3(Nothing)
That's the converted to:
ToString2(Me)
ToString3(Me, Nothing)
That doesn't happen with ToString1
(which is treated as a regular shared module-wide method) because Me
isn't implicitly convertible to Integer
. (I don't know the details of method invocation in VB, but it sounds like extension methods are searched before module-wide shared methods invoked in the regular way.)
See more on this question at Stackoverflow