.net vb.net extension-methods nullreferenceexception

¿Por qué este método de extensión arroja una NullReferenceException en VB.NET?



extension-methods (4)

Por experiencia previa, tenía la impresión de que es perfectamente legal (aunque quizás no aconsejable) llamar a los métodos de extensión en una instancia nula. Entonces en C #, este código compila y ejecuta:

// code in static class static bool IsNull(this object obj) { return obj == null; } // code elsewhere object x = null; bool exists = !x.IsNull();

Sin embargo, estaba armando un pequeño paquete de código de ejemplo para los demás miembros de mi equipo de desarrollo (acabamos de actualizar a .NET 3.5 y se me ha asignado la tarea de poner al equipo al día sobre algunas de las nuevas funciones). disponible para nosotros), y escribí lo que pensé que era el equivalente VB.NET del código anterior, solo para descubrir que en realidad arroja una NullReferenceException . El código que escribí fue este:

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

El depurador se detiene allí mismo, como si hubiera llamado a un método de instancia. ¿Estoy haciendo algo mal (por ejemplo, hay alguna diferencia sutil en la forma en que definí el método de extensión entre C # y VB.NET)? ¿En realidad no es legal llamar a un método de extensión en una instancia nula en VB.NET, aunque es legal en C #? (Hubiera pensado que esto era algo de .NET en lugar de algo específico del idioma, pero quizás estaba equivocado).

¿Alguien puede explicarme esto?


Actualizar:

La respuesta a continuación parece ser específica para el caso en que System.Object se extienda. Al extender otras clases, no hay NullReferenceException en VB.

Este comportamiento es por diseño por el motivo indicado en este problema de Connect :

VB le permite llamar a los métodos de extensión definidos en Object, pero solo si la variable no está tipada estáticamente como Object.

El motivo es que VB también admite la vinculación tardía, y si nos vinculamos a un método de extensión cuando realiza una llamada fuera de una variable declarada como Objeto, entonces es ambiguo si está intentando llamar a un método de extensión oa un método de extensión diferente. método vinculado con el mismo nombre.

Teóricamente podríamos permitir esto con Strict On, pero uno de los principios de Option Strict es que no debería cambiar la semántica de su código. Si esto está permitido, entonces cambiar la configuración de Option Strict podría causar un rebinding silencioso a un método diferente, lo que da como resultado un comportamiento de tiempo de ejecución totalmente diferente.

Ejemplo:

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

De hecho, el compilador de VB crea una llamada de enlace tardía en caso de que su variable esté tipada estáticamente como 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


No puede extender el tipo de objeto en VB.NET.

Principalmente, no permitimos que los métodos de extensión se desactiven de ninguna expresión que esté tipeada estáticamente como "Objeto". Esto fue necesario para evitar que los códigos de extensión destruyan cualquier código de límite tardío existente que haya escrito.

Referencia:


Parece que el problema es que el objeto es nulo. Además, si prueba algo como lo siguiente, obtendrá una excepción que dice que String no tiene un método de extensión llamado IsNull.

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

Creo que cualquier valor que pongas en exampleObject, el framework sabe de qué tipo es. Personalmente, evitaría los métodos de extensiones en la clase Object, no solo en VB sino también en CSharp.


Parece ser algo peculiar con Object, posiblemente un error en VB o una limitación en el compilador, ¡podría necesitar su Santidad Jon Skeet para comentar!

Básicamente, parece estar intentando enlazar tarde la llamada IsNull en tiempo de ejecución, en lugar de llamar al método de extensión, que causa la NullReferenceException. Si activa Option Strict, verá esto en el momento del diseño con los garabatos rojos.

Cambiar exampleObject a algo que no sea el Object en sí mismo permitirá que su código de muestra funcione, incluso si el valor de dicho tipo es Nothing.