2011-12-20 16 views
7

Sono curioso di sapere perché il seguente codice funziona (eseguito nel debugger VS):Perché un int? impostato su null ha proprietà di istanza?

int? x = null; 
null 
x.HasValue 
false 

Se x è davvero nulla, cosa esempio fa riferimento a HasValue? HasValue implementato come metodo di estensione, oppure il compilatore è un caso speciale per farlo funzionare magicamente?

+1

C'è un trucco davvero orribile per casi speciali con tipi nullable. Il runtime assegnerà automaticamente il loro valore interno durante la boxe. Ciò significa che "((object) x) .HasValue" genererà una NullReferenceException. È orribile. –

+0

@KennetBelenky effettivamente '((oggetto) x). HasValue' non verrà compilato, perché non esiste una proprietà HasValue sull'oggetto. Penso che intendessi dire che '((oggetto) x) .ToString()' (o '.GetHashCode()' o '.Equalities (qualcosa)') genererà. – phoog

+0

@phoog, oops Ero un po 'frettoloso nella mia descrizione. Hai ragione, la mia descrizione non è sintatticamente valida. Il punto, tuttavia, è che nel momento in cui lanci un Nullable su un oggetto, non è più un Nullable. Nessun altro tipo nel framework .Net ha quella proprietà. –

risposta

9

Perché x non è un tipo di riferimento. Lo ? è solo zucchero sintattico per Nullable<T>, che è un struct (tipo di valore).

+0

Questo ha più senso. Anche gli operatori di confronto e assegnazione verrebbero sovraccaricati. Comunque, la cosa che mi confonde è se guardo 'x' sotto la finestra Watch, mostra' null' (che dovrebbe essere impossibile se questo è davvero un tipo di valore). Il caso speciale del debugger VS questo? –

+0

@ Mike: Beh, il debugger VS è una strana bestia. Ma penso che in questo caso è semplicemente usando il metodo 'ToString()' di Nullable. – Cameron

+0

@Cameron, ToString() per un valore nullo è documentato per restituire una stringa emtpy se il valore è "uguale" a null. Se dovessi scommettere un nichel, direi che il debugger lo sta inscatolando, il che produrrebbe un valore nullo. –

1

Quando si utilizza int? x = null quindi x viene assegnata una nuova istanza di Nullable<int> e il valore ist è impostato su null.

Non conosco esattamente le parti interne, ma suppongo che l'operatore di assegnazione stesso sia un po 'sovraccarico.

5

int? è in realtà una struttura Nullable<int>. Quindi, il tuo x non può essere nullo, perché è sempre l'istanza di una struttura.

0

Il tipo nullable (in questo caso: nullable int) ha una proprietà HasValue che è booleana. Se HasValue è True, la proprietà Value (di tipo T, in questo caso, un int) avrà un valore valido.

1

Un tipo annullabile non è in realtà null dato che ancora non ottiene intorno al fatto che i tipi di valore non possono essere null. È, invece, un riferimento alla struttura Nullable<> (che è anche un tipo di valore e non può essere null).

Ulteriori informazioni here.

Fondamentalmente, si fa sempre riferimento a un'istanza di qualcosa quando si utilizza un tipo nullable. Le informazioni predefinite restituite da tale riferimento sono il valore memorizzato (o null se non è presente alcun valore memorizzato).

1

Nullable non è veramente un tipo di riferimento e i suoi metodi di istanza sono uno dei luoghi in cui viene visualizzato. Fondamentalmente, è un tipo struct contenente un flag booleano e un valore del tipo di cui è un valore nullo di. Il caso speciale di lingue diversi operatori [da sollevare, o da considerare (bool?) Null false in alcuni casi] e il letterale nulla, e il box di casi speciali di runtime, ma a parte questo è solo una struttura.

1

È un tipo completamente nuovo. Nullable è non T.

Quello che hai è una classe qualcosa di generico come questo:

public struct Nullable<T> 
{ 
    public bool HasValue { get { return Value != null; } } 
    public T Value { get; set; } 
} 

Sono sicuro che c'è di più ad esso (in particolare nel getter e setter, ma questo è tutto in un . soldoni

+1

Ci sono anche operatori espliciti, ma questo è fondamentalmente. Jon Skeet fa un ottimo lavoro di spiegazione di 'Nullable ' in _C# in Depth_. –

+0

In realtà, è una 'struct' generica, non una classe. –

+0

- Ignora questo commento, Ninja'd di Doc Brown – DaveShaw

4

mano sventolando risposta:. struct nullable sono magici

Longer risposta:. Null non è in realtà ciò che è rappresentato dal valore Quando si assegna null a una struct annullabile, ciò che vedrete accadere dietro t le scene sono diverse.

int? val = null; // what you wrote 
Nullable<Int32> val = new Nullable<Int32>(); // what is actually there 

In questo caso, viene creata un'istanza della struct che ha la T Value impostato ad un valore predefinito e la bool HasValue proprietà impostata su false.

L'unica volta che effettivamente ottenere un nullo riferimento da un Nullable<T> è quando è inscatolato, come Nullable<T> scatole direttamente T o nullo, a seconda se il valore è impostato.

2

Ci sono diversi significati per null.

Uno in linguaggi di programmazione che presentano variabili e memoria in un modo basato su puntatore (che include i riferimenti di C# anche se nasconde alcuni dettagli) è "questo non punta a nulla".

Un altro è "questo non ha un valore significativo".

Con i tipi di riferimento, spesso usiamo il primo per rappresentare quest'ultimo. Potremmo usare string label = null per significare "nessuna etichetta significativa, ma rimane comunque anche una questione di cosa sta succedendo in termini di cosa c'è in memoria e cosa lo sta indicando.Eppure, è dannatamente utile, che peccato che noi non poteva farlo con int e DateTime in C# 1.1

Questo è quello che Nullable<T> fornisce, un mezzo per dire "nessun valore significativo", ma a livello di sotto di essa non è null nello stesso modo in una stringa null è (a meno che non è stato assegnato a null ed è uguale a null, quindi è logicamente nullo e nullo in base ad altre semantiche, ma non è nullo nella differenza di implementazione "non fa riferimento a nulla" tra i tipi di riferimento e di valore

È solo l'aspetto "non fa riferimento a nulla" del tipo di riferimento null che impedisce di chiamare i metodi di istanza su di esso.

E in realtà, anche questo non è assolutamente vero. IL ti consente di chiamare i metodi di istanza su un riferimento null e finché non interagisce con nessun campo, funzionerà. Non può funzionare se ha bisogno (direttamente o indirettamente) quei campi dal momento che non esistono su un riferimento e nulla, ma potrebbe chiamare null.FineWithNull() se questo metodo è stato definito come:

int FineWithNull() 
{ 
    //note that we don't actually do anything relating to the state of this object. 
    return 43; 
} 

Con C# è stato deciso per non farlo, ma non è una regola per tutti i .NET (penso che F # lo consenta, ma non ne sono sicuro, so che il C++ non gestito lo ha permesso ed è stato utile in alcuni casi molto rari).

+0

Credo che C# lo permetta nel caso in cui il metodo sia un metodo di estensione della classe. Sicuramente un riassunto interessante su null però, '+ 1'! –

+0

@MikeChristensen Hai ragione su questo, anche se in quel caso sta succedendo che C# sta permettendo al primo argomento di chiamare un metodo statico come null - come ci si aspetterebbe in qualsiasi linguaggio con un concetto di null - e quindi permettendolo essere chiamato con la stessa sintassi dei metodi di istanza. –

+0

@MikeChristensen C'è un'ironia interessante che C# impone di non consentire le chiamate al metodo su null usando 'callvirt' IL per la maggior parte delle chiamate sia virtuali che virtuali -' callvirt' include un controllo nullo. Ci sono ancora alcune volte in cui viene usato 'call', e uno è quando un valuetype ha scavalcato un metodo virtuale - non può essere nullo e non può essere ulteriormente derivato così' call' avrà esattamente la stessa semantica di 'callvirt' . Ora, questo di per sé dimostra che virtuale vs non virtuale non è l'unico modo per giudicare se chiamare "callvirt" o "call", ma è ironico che in C# l'uso è quasi opposto al nome. –

Problemi correlati