2010-03-08 19 views
23

Por experiencia anterior, 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. Así que en C#, este código se compila y se ejecuta:¿Por qué este método de extensión arroja una NullReferenceException en VB.NET?

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

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

Sin embargo, sólo estaba preparando una pequeña suite de código de ejemplo para los demás miembros de mi equipo de desarrollo (que sólo pasaron a .NET 3.5 y no tengo se le ha asignado la tarea de poner al equipo al día sobre algunas de las nuevas funciones disponibles para nosotros), y escribí lo que pensó que era el equivalente de VB.NET del código anterior, solo para descubrir que en realidad arroja un NullReferenceException . El código que escribí fue el siguiente:

' 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 justo allí, 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)? ¿Es realmente no 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 me puede explicar esto?

+1

es el método EsNulo sólo un ejemplo, o son lo que realmente desea utilizar x.IsNull() en lugar de 'x Is Nothing' o 'x == null'? – jrummell

+0

@jrummell: Es solo un ejemplo. Como mencioné, estaba escribiendo un código de ejemplo para ilustrar cómo funcionan los métodos de extensión para algunos de los miembros de mi equipo. Me propuse que este método incluyera un comentario del tipo "En realidad, puedes hacerlo con métodos de extensión, aunque no lo recomendaría", solo para mostrar cómo bajo un sistema de extensión es realmente solo un elemento estático (Compartido).) método. Pero luego descubrí que * no * podía hacerlo en VB, lo que me sorprendió. –

+1

Como se ha encontrado, esto se debe a la compatibilidad con el enlace tardío ... como usted (Dan). No me di cuenta de que VB tenía soporte para el enlace tardío, supongo que es una compatibilidad con VB6. Como un pequeño aparte, si está buscando establecer algunos estándares/entrenamiento para otros desarrolladores, quizás desee ordenar Option Strict ya que creo que ayuda a resolver muchos problemas potenciales. YMMV por supuesto. –

Respuesta

13

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

Principalmente, no permitimos que los métodos de extensión que ser cancelado de cualquier expresión que se escribe de forma estática 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:

+0

Ah, eso tiene algún sentido ... aunque estoy continuamente molesto, pero las diferencias/incoherencias entre C#/VB. La frase clave a tomar de los artículos vinculados es; "cuando especifica objeto, significa" tomar cualquier cosa * otro * que un objeto ". Impresionante. 8-) –

+0

@roygbiv: Como alguien que no codifica demasiado a menudo en VB.NET, honestamente no tenía ni idea Llamadas a métodos vinculados tales como las discutidas en el enlace fueron incluso posibles. (Hubiera supuesto que las capacidades de enlace tardío de VB.NET eran significativamente más pequeñas). Así que gracias por enseñarme algo nuevo y por ser el primero en dar con lo que pienso es la respuesta "verdadera" aquí. –

+1

@DanTao: Deseo que VB.NET permita declarar una variable como 'System.Object' y hacer que se comporte simplemente como una referencia de clase cuyo tipo de base sea' System.Object' , sin el manejo especial asociado con 'Object'. – supercat

0

Parece que el problema es que el objeto es nulo. Además, si intenta algo como lo siguiente, obtendrá una excepción diciendo esa cadena no tiene un método de extensión llamado EsNulo

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

Creo que cualquier valor que está poniendo en exampleObject, el marco sabe qué tipo es. Yo personalmente evitaría métodos extensiones en la clase Object, no sólo en VB, sino también en CSharp

8

Actualización:

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

Este comportamiento es por diseño por la razón indicada en este Connect issue:

VB le permite llamar a los métodos de extensión definidos el objeto, pero sólo si la variable no es estática escrito como objeto.

La razón es VB también es compatible con enlace tardío, y si se unen a un método extensión cuando se realiza una llamada fuera una variable declarada como objeto, entonces es ambigua o no que está tratando de llamar un método de extensión o un método de enlace posterior diferente con el mismo nombre.

Teóricamente podría permitir esto con Strict On, pero uno de los principios de Option Strict es que no debe cambiar la semántica de su código. Si esto está permitido, entonces cambiando la configuración de Opción estricta podría causar un reenlace silencioso a un método diferente , resultando en total comportamiento de tiempo de ejecución 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 unión tarde en caso de que su variable es de tipo estático 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 
+0

Me pregunto: ¿vb llama a los métodos de extensión el modo C# si están en una biblioteca escrita en C#? –

+0

y @Joel: Acabo de probar esto: coloque el método de extensión en una biblioteca C#, lo llamó desde VB. El código VB todavía lanzó una 'NullReferenceException'. Parece que Gareth está en algo: este problema solo existe cuando el método de extensión se aplica a 'System.Object' en lugar de a algo más específico. –

+0

De hecho, es el tipo de objeto con el que intenta llamar al método de extensión, no el tipo que espera la función. Es decir, el IsNull (ByVal Obj como Objeto) funciona con Dim A As String ... A.IsNull está bien, incluso donde A es Nothing. roygbiv parece tener los enlaces pertinentes sobre por qué. –

3

parece ser algo peculiar con Objeto, posiblemente un error en VB o una limitación en el compilador, ¡podría necesitar a 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.

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

+0

+1. El método de extensión permite que nada se use solo si el tipo no es Object. Por lo tanto, parece estar relacionado con la naturaleza de Object en tiempo de ejecución. –

+0

Ja, wow ... ahora eso es raro. –

Cuestiones relacionadas