提问人:Dan Tao 提问时间:3/9/2010 最后编辑:Dan Tao 更新时间:3/9/2010 访问量:3011
为什么此扩展方法在 VB.NET 中引发 NullReferenceException?
Why does this extension method throw a NullReferenceException in VB.NET?
问:
根据以前的经验,我的印象是在 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 之间定义扩展方法的方式是否存在一些细微差别)?尽管在 C# 中是合法的,但在 VB.NET 中对 null 实例调用扩展方法实际上并不合法吗?(我本来以为这是 .NET 的东西,而不是特定于语言的东西,但也许我错了。
谁能向我解释一下这个?
答:
似乎问题在于该对象为空。 此外,如果尝试如下操作,则会出现一个异常,指出 String 没有名为 IsNull 的扩展方法
Dim exampleObject As Object = "Test"
Dim text As String = exampleObject.IsNull()
我认为无论您将什么值放入 exampleObject,框架都知道它是什么类型。我个人会避免在 Object 类上使用扩展方法,不仅在 VB 中,而且在 CSharp 中也是如此
更新:
下面的答案似乎是针对扩展的情况。扩展其他类时,VB 中没有。System.Object
NullReferenceException
此行为是设计使然,原因如下:
VB 允许您调用在 Object 上定义的扩展方法,但仅限于 如果变量不是静态的 类型化为 Object。
原因是 VB 也支持后期绑定,如果我们绑定到 拨打电话时的分机方法 关闭声明为 Object 的变量, 那么是否模棱两可 您正在尝试呼叫分机 方法或其他后期绑定 方法。
从理论上讲,我们可以用 Strict On 允许这样做,但其中一个 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
事实上,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
评论
NullReferenceException
System.Object
System.Object
这似乎是 Object 的一些古怪之处,可能是 VB 中的错误或编译器中的限制,可能需要他的尊者 Jon Skeet 来评论!
基本上,它似乎试图在运行时延迟绑定 IsNull 调用,而不是调用扩展方法,这会导致 NullReferenceException。如果打开 Option Strict,您将在设计时看到带有红色波浪线的显示。
将 exampleObject 更改为 Object 本身以外的其他内容将允许示例代码工作,即使所述类型的值为 Nothing。
评论
不能在 VB.NET 中扩展对象类型。
主要是,我们不允许从任何静态类型为“Object”的表达式中调用扩展方法。这是必要的,以防止您可能编写的任何现有后期绑定代码被扩展方法破坏。
参考:
评论
System.Object
System.Object
Object
评论