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

时间:2010-03-08 16:05:35

标签: .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();

但是,我只是为我的开发团队的其他成员组装了一小组示例代码(我们刚刚升级到.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()

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

有人可以向我解释这个吗?

4 个答案:

答案 0 :(得分:13)

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

  
    

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

  

参考:

答案 1 :(得分:8)

更新:

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

此行为是出于设计理由,原因在于此Connect issue

  

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

实际上,如果你的变量被静态输入为Object,VB编译器会创建一个后期绑定调用:

.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

答案 2 :(得分:3)

似乎有些古怪的Object,可能是VB中的错误或编译器的限制,可能需要他的圣洁Jon Skeet发表评论!

基本上它似乎是试图在运行时延迟绑定IsNull调用,而不是调用扩展方法,这会导致NullReferenceException。如果你打开Option Strict,你会在设计时看到这个红色波浪线。

将exampleObject更改为除Object之外的其他内容将允许您的示例代码工作,即使所述类型的值为Nothing。

答案 3 :(得分:0)

似乎问题是该对象为null。 此外,如果你尝试类似下面的内容,你会得到一个异常,说String没有名为IsNull的扩展方法

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

我认为无论你在exampleObject中添加什么价值,框架都知道它是什么类型。我个人避免在Object类上使用扩展方法,不仅在VB中,而且在CSharp