为什么这个扩展方法在VB.NET中抛出NullReferenceException?

Dan*_*Tao 23 .net vb.net extension-methods nullreferenceexception

从以前的经验来看,我一直认为在null实例上调用扩展方法是完全合法的(尽管可能不可取).所以在C#中,这段代码编译并运行:

// code in static class
static bool IsNull(this object obj) {
    return obj == null;
}

// code elsewhere
object x = null;
bool exists = !x.IsNull();
Run Code Online (Sandbox Code Playgroud)

但是,我只是为我的开发团队的其他成员组建了一小组示例代码(我们刚刚升级到.NET 3.5,我已经被分配了让团队加快一些新功能的任务我们可以使用),我写了我认为是上面代码的VB.NET等价物,但却发现它实际上抛出了一个NullReferenceException.我写的代码是这样的:

' code in module '
<Extension()> _
Function IsNull(ByVal obj As Object) As Boolean
    Return obj Is Nothing
End Function

' code elsewhere '
Dim exampleObject As Object = Nothing
Dim exists As Boolean = Not exampleObject.IsNull()
Run Code Online (Sandbox Code Playgroud)

调试器就在那里停止,好像我调用了一个实例方法.我做错了什么(例如,我在C#和VB.NET之间定义扩展方法的方式有一些细微差别)吗?在VB.NET中调用null实例上的扩展方法实际上是合法的,尽管它在C#中是合法的吗?(我原以为这是一个.NET的东西,而不是语言特定的东西,但也许我错了.)

任何人都可以向我解释这个吗?

小智 13

您无法在VB.NET中扩展对象类型.

主要是,我们不允许从静态类型为"Object"的任何表达式中调用扩展方法.这对于防止您编写的任何现有的后期绑定代码被扩展方法破坏是必要的.

参考:


Dir*_*mar 8

更新:

下面的答案似乎特定于System.Object扩展的情况.在扩展其他类时NullReferenceException,VB中没有.

出于此连接问题中所述的原因,此行为是出于设计原因:

VB允许您调用在Object上定义的扩展方法,但前提是该变量不是静态类型为Object.

原因是VB也支持后期绑定,如果我们在声明为Object的变量上调用时绑定一个扩展方法,那么无论你是否试图调用扩展方法或者不同的后者,它都是不明确的具有相同名称的绑定方法.

从理论上讲,我们可以使用Strict On来实现这一点,但Option Strict的一个原则是它不应该改变代码的语义.如果允许这样做,那么更改Option Strict设置可能会导致对其他方法进行静默重新绑定,从而导致完全不同的运行时行为.

例:

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
Run Code Online (Sandbox Code Playgroud)

实际上,如果您的变量被静态类型化,VB编译器会创建一个后期绑定调用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
Run Code Online (Sandbox Code Playgroud)