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

发布于 2024-08-23 20:59:48 字数 882 浏览 13 评论 0原文

根据以前的经验,我一直认为在空实例上调用扩展方法是完全合法的(尽管可能不建议)。因此,在 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 之间定义扩展方法的方式是否存在一些细微的差异)?尽管在 C# 中是合法的,但在 VB.NET 中对 null 实例调用扩展方法实际上合法吗? (我本以为这是 .NET 的事情,而不是特定于语言的事情,但也许我错了。)

有人能给我解释一下吗?

From previous experience I had been under the impression that it's perfectly legal (though perhaps not advisable) to call extension methods on a null instance. So in C#, this code compiles and runs:

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

// code elsewhere
object x = null;
bool exists = !x.IsNull();

However, I was just putting together a little suite of example code for the other members of my development team (we just upgraded to .NET 3.5 and I've been assigned the task of getting the team up to speed on some of the new features available to us), and I wrote what I thought was the VB.NET equivalent of the above code, only to discover that it actually throws a NullReferenceException. The code I wrote was this:

' 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()

The debugger stops right there, as if I'd called an instance method. Am I doing something wrong (e.g., is there some subtle difference in the way I defined the extension method between C# and VB.NET)? Is it actually not legal to call an extension method on a null instance in VB.NET, though it's legal in C#? (I would have thought this was a .NET thing as opposed to a language-specific thing, but perhaps I was wrong.)

Can anybody explain this one to me?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暖风昔人 2024-08-30 20:59:48

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

<块引用>

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

参考:

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:

更新:

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

此行为是设计使然,原因如下 连接问题

VB 允许您调用对象上定义的扩展方法,但仅限于
如果变量不是静态的
输入为对象。

原因是VB也支持后期绑定,如果我们绑定到一个
拨打电话时的分机方法
关闭声明为对象的变量,
那么是否是模棱两可的
您正在尝试呼叫分机
方法或不同的后期绑定
具有相同名称的方法。

理论上我们可以通过 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

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
小霸王臭丫头 2024-08-30 20:59:48

Object 似乎有点奇怪,可能是 VB 中的错误或编译器中的限制,可能需要 Jon Skeet 阁下发表评论!

基本上,它似乎试图在运行时后期绑定 IsNull 调用,而不是调用扩展方法,这会导致 NullReferenceException。如果您打开 Option Strict,您将在设计时看到带有红色波浪线的情况。

将 exampleObject 更改为 Object 本身以外的其他对象将使您的示例代码能够工作,即使所述类型的值为 Nothing。

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.

甜扑 2024-08-30 20:59:48

看来问题是该对象为空。
另外,如果您尝试类似以下内容,您会得到一个异常,指出 String 没有名为 IsNull 的扩展方法。

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

我认为无论您将值放入 exampleObject 中,框架都知道它是什么类型。我个人会避免在 Object 类上扩展方法,不仅在 VB 中而且在 CSharp 中也是如此

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文