Why does this extension method throw a NullReferenceException in VB.NET?

允我心安 提交于 2019-11-27 20:30:56

You cannot extend the object type in VB.NET.

Mainly, we do not allow extension methods to be called off of any expression that is statically typed as "Object". This was necessary to prevent any existing late bound code you may have written from being broken by extension methods.

Reference:

Update:

The answer below seems to be specific to the case the System.Object is extended. When extending other classes there is no NullReferenceException in VB.

This behavior is by design for the reason stated in this Connect issue:

VB allows you to call extension methods defined on Object, but only if the variable is not statically typed as Object.

The reason is VB also supports late-binding, and if we bind to an extension method when you make a call off a variable declared as Object, then it's ambigous whether or not you're trying to call an extension method or a different late-bound method with the same name.

Theoretically we could allow this with Strict On, but one of the principles of Option Strict is that it should not change the semantics of your code. If this was allowed then changing your Option Strict setting could cause a silent rebinding to a different method, resulting in totally different runtime behavior.

Example:

Imports System.Runtime.CompilerServices

Module Extensions
    <Extension()> _
    Public Function IsNull(ByVal obj As Object) As Boolean
        Return obj Is Nothing
    End Function

    <Extension()> _
    Public Function IsNull(ByVal obj As A) As Boolean
        Return obj Is Nothing
    End Function

    <Extension()> _
    Public Function IsNull(ByVal obj As String) As Boolean
        Return obj Is Nothing
    End Function

End Module

Class A
End Class

Module Module1

    Sub Main()
        ' works
        Dim someString As String = Nothing
        Dim isStringNull As Boolean = someString.IsNull()

        ' works
        Dim someA As A = Nothing
        Dim isANull As Boolean = someA.IsNull()

        Dim someObject As Object = Nothing
        ' throws NullReferenceException
        'Dim someObjectIsNull As Boolean = someObject.IsNull()

        Dim anotherObject As Object = New Object
        ' throws MissingMemberException
        Dim anotherObjectIsNull As Boolean = anotherObject.IsNull()
    End Sub

End Module

In fact, the VB compiler creates a late binding call in case your variable is statically typed as Object:

.locals init ([0] object exampleObject, [1] bool exists)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldnull
  IL_0004:  ldstr      "IsNull"
  IL_0009:  ldc.i4.0
  IL_000a:  newarr     [mscorlib]System.Object
  IL_000f:  ldnull
  IL_0010:  ldnull
  IL_0011:  ldnull
  IL_0012:  call       
     object [Microsoft.VisualBasic]Microsoft.VisualBasic.
       CompilerServices.NewLateBinding::LateGet(
        object,
        class [mscorlib]System.Type,
        string,
        object[],
        string[],
        class [mscorlib]System.Type[],
        bool[])
  IL_0017:  call       object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Operators::NotObject(object)
  IL_001c:  call       bool [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToBoolean(object)
  IL_0021:  stloc.1

It seems to be something quirky with Object, possibly a bug in VB or a limitation in the compiler, might need his Holiness Jon Skeet to comment!

Basically it appears to be trying to late bind the IsNull call at runtime, rather than call the extension method, which causes the NullReferenceException. If you turn on Option Strict you'll see this at design time with the red squiggles.

Changing exampleObject to something other than Object itself will allow your sample code to work, even if the value of said type is Nothing.

It seems the problem is that the object is null. Also, if you try something like the following, you'll get an exception saying that String does not have an extension method called IsNull

Dim exampleObject As Object = "Test"
Dim text As String = exampleObject.IsNull()

I think whatever value you are putting into exampleObject, the framework knows what type it is. I'd personally avoid extensions methods on the Object class, not just in VB but also in CSharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!