2010-03-08 16 views
23

Dall'esperienza precedente avevo avuto l'impressione che fosse perfettamente legale (anche se forse non consigliabile) chiamare i metodi di estensione su un'istanza nulla. Quindi, in C#, questo codice viene compilato ed eseguito:Perché questo metodo di estensione genera una NullReferenceException in VB.NET?

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

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

Tuttavia, stavo solo mettendo insieme una piccola suite di codice di esempio per gli altri membri del mio team di sviluppo (siamo appena trasferiti in .NET 3.5 e ho stato assegnato il compito di ottenere il team su alcune delle nuove funzionalità a nostra disposizione), e ho scritto quello che ho pensato era l'equivalente VB.NET del codice sopra, solo per scoprire che in realtà getta un NullReferenceException . Il codice che ho scritto era questo:

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

Il debugger si ferma proprio lì, come se avessi chiamato un metodo di istanza. Sto facendo qualcosa di sbagliato (ad esempio, c'è una sottile differenza nel modo in cui ho definito il metodo di estensione tra C# e VB.NET)? È in realtà non legale legale chiamare un metodo di estensione su un'istanza null in VB.NET, sebbene sia legale in C#? (Avrei pensato che si trattasse di una cosa .NET rispetto a una cosa specifica della lingua, ma forse mi sbagliavo.)

Qualcuno può spiegarmelo?

+1

è il metodo IsNull solo un esempio o sono si fa effettivamente desidera utilizzare x.IsNull() invece di 'x Is Nothing' o 'x == null'? – jrummell

+0

@jrummell: è solo un esempio. Come ho detto, stavo scrivendo un codice di esempio per illustrare come funzionano i metodi di estensione per alcuni membri del mio team. Intendevo che questo metodo includesse un commento sulla falsariga di "In realtà puoi farlo con i metodi di estensione, anche se non lo consiglierei" - solo per mostrare come sotto un cappuccio un metodo di estensione sia in realtà solo una statica) metodo. Ma poi ho scoperto che non potevo * farlo in VB, il che mi ha sorpreso. –

+1

Come è stato rilevato, questo è dovuto al supporto per l'associazione tardiva ... come te (Dan) Non mi rendevo conto che VB aveva tale supporto per l'associazione tardiva, suppongo che sia una compatibilità con VB6. Per fare un po 'di attenzione, se stai cercando di definire alcuni standard/formazione per altri sviluppatori potresti voler imporre Option Strict perché ritengo che aiuti a raccogliere molti potenziali problemi. YMMV naturalmente. –

risposta

13

Non è possibile estendere il tipo di oggetto in VB.NET.

Principalmente, non permettiamo i metodi di estensione di essere chiamato fuori di ogni espressione che viene digitato in modo statico come "Oggetto". Questo era necessario per impedire che eventuali codici esistenti in ritardo che potresti aver scritto fossero stati interrotti dai metodi di estensione.

Riferimento:

+0

Ah, questo ha un senso ... anche se sono continuamente seccato ma le differenze/incongruenze tra C#/VB. La frase chiave da prendere da quegli articoli collegati è; "quando specifichi l'oggetto, significa" prendi qualsiasi cosa * altro * di un oggetto. " Impressionante. 8-) –

+0

@roygbiv: come qualcuno che non codifica troppo spesso in VB.NET, onestamente non avevo idea in ritardo anche le chiamate al metodo vincolate come quelle discusse nel collegamento erano possibili (avrei immaginato che le capacità di binding tardive di VB.NET fossero significativamente più piccole). Quindi grazie per avermi insegnato qualcosa di nuovo e per essere stato il primo a colpire quello che penso è la risposta "vera" qui –

+1

@DanTao: Vorrei che VB.NET consentisse a uno di dichiarare una variabile come "System.Object" e si comportasse semplicemente come un riferimento di classe il cui tipo di base è "System.Object". , senza la gestione speciale associata a 'Object'. – supercat

0

Sembra che il problema sia che l'oggetto è nullo. Inoltre, se si cerca qualcosa di simile a quanto segue, si otterrà un'eccezione dicendo che String non ha un metodo di estensione chiamato IsNull

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

Penso che qualsiasi valore che si sta mettendo in exampleObject, il quadro sa che tipo è. Eviterei personalmente metodi estensioni sulla classe Object, non solo in VB, ma anche in CSharp

8

Aggiornamento:

La risposta qui sotto sembra essere specifico per il caso del System.Object è esteso. Quando si estendono altre classi, non c'è NullReferenceException in VB.

Questo comportamento è di progettazione per la ragione previsto nel presente Connect issue:

VB consente di chiamare metodi di estensione definiti su oggetti, ma solo se la variabile non è staticamente digitato come oggetto.

Il motivo è VB supporta anche late-binding, e se ci leghiamo ad un metodo di interno quando si effettua una chiamata fuori una variabile dichiarata come oggetto, allora è ambiguo o meno si sta cercando di chiamare un metodo di estensione o un diverso metodo tardivo con lo stesso nome.

Teoricamente ci potrebbe consentire a questo con Strict On, ma uno dei principi della Option Strict è che non dovrebbe cambiare la semantica di codice. Se ciò è stato consentito, è possibile modificare Option Strict con in modo da eseguire una riconciliazione silenziosa con un metodo diverso , determinando un comportamento di runtime totalmente diverso.

Esempio:

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 

Infatti, il compilatore VB crea una chiamata vincolante in ritardo nel caso in cui la variabile è staticamente tipizzato come 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

Mi chiedo: i metodi di estensione chiamata vb vanno in C# se sono in una libreria scritta in C#? –

+0

e @Joel: l'ho appena testato, inserire il metodo di estensione in una libreria C#, chiamato da VB. Il codice VB lanciava ancora un 'NullReferenceException'. Sembra che Gareth sia su qualcosa: questo problema esiste solo quando il metodo di estensione è applicato a "System.Object" anziché a qualcosa di più specifico. –

+0

In effetti, è il tipo di oggetto con cui stai cercando di chiamare il metodo di estensione, non il tipo che la funzione si aspetta. Cioè, IsNull (Oggetto ByVal come Oggetto) funziona con Dim A As String ... A.Non va bene, anche dove A è Nothing. roygbiv sembra avere i collegamenti pertinenti sul perché. –

3

Sembra essere qualcosa di eccentrico con L'oggetto, possibilmente un bug in VB o una limitazione nel compilatore, potrebbe aver bisogno di commentare la sua Holiness Jon Skeet!

In genere sembra che si stia tentando di associare in ritardo la chiamata IsNull in fase di esecuzione, anziché chiamare il metodo di estensione, che causa NullReferenceException. Se attivi Option Strict lo vedrai in fase di progettazione con gli scarabocchi rossi.

Cambiare exampleObject in qualcosa di diverso da Object stesso consentirà al codice di esempio di funzionare, anche se il valore di tale tipo è Nothing.

+0

+1. Il metodo di estensione non consente l'utilizzo di Nothing solo se il tipo non è Object. Quindi, sembra essere collegato alla natura di Object in fase di esecuzione. –

+0

Ha, wow ... ora è strano. –

Problemi correlati