2011-11-20 12 views
50

D orecchio tutto, mi chiedo qual è il tipo di letterale null in C#?Qual è il tipo di valore letterale nulla?

In Java, il null letterale is of the special null type:

C'è anche una speciale tipo nullo, il tipo dell'espressione null, che non ha nome. Poiché il tipo nullo non ha nome, è impossibile dichiarare una variabile del tipo nullo o eseguire il cast sul tipo nullo. Il riferimento null è l'unico valore possibile di un'espressione di tipo nullo. Il riferimento null può essere sempre assegnato a qualsiasi tipo di riferimento.

In C++ 11, c'è nullptr (la versione consigliata del vecchio compagno NULL), che is of type std::nullptr_t.

Ho cercato MSDN su C#, ma il specification non sembra dire nulla al riguardo.

+3

Non descriverei esattamente null avendo un tipo. – ChaosPandion

+0

@ChaosPandion: così, in C# non l'espressione _every_ ha un tipo? – Vlad

+1

Beh, io sono apparentemente sbagliato. – ChaosPandion

risposta

53

Secondo il ECMA C# language specification:

9.4.4.6 Il nulla letterale:

Il tipo di un nullo letterale è il tipo nullo (§11.2.7).

11.2.7 Il tipo nullo:

Il nulla letterale (§9.4.4.6) valuta al valore nullo, che viene usato per indicare un riferimento non punta a qualsiasi oggetto o matrice, oppure l'assenza di un valore. Il tipo null ha un valore singolo, che è il valore nullo . Quindi un'espressione il cui tipo è il tipo null può valutare solo al valore nullo. Non è possibile scrivere in modo esplicito il tipo null e, quindi, non è possibile utilizzarlo in un tipo dichiarato. Inoltre, il tipo di nulla può mai essere del tipo desunti per un parametro di tipo (§25.6.4)

Quindi, per rispondere alla tua domanda, nulla è il suo tipo - il tipo di nulla.

Anche se è strano come non è menzionato nel C# 4.0 language specification o C# 3.0 language specification ma è menzionato nel overview of C# 3.0, il ECMA C# language specification e la C# 2.0 language specification.

+0

Stranamente, il tuo riferimento ai punti standard ECMA nel documento che non è lo stesso di MSDN: http://msdn.microsoft.com/en-us/library/ms228593.aspx – Vlad

+0

@Vlad che è abbastanza interessante, potrebbe valere la pena di vedere cosa [Eric Lippert] (http://blogs.msdn.com/b/ericlippert/) ha da dire al riguardo. –

+0

Penso che il ragionamento per avere un tipo separato per 'null' sia lo stesso di Java/C++. Mi chiedo tuttavia perché questo non ha trovato la sua strada in MSDN. – Vlad

42

AGGIORNAMENTO: This question was the subject of my blog in July 2013. Grazie per l'ottima domanda!


risposta di J.Kommer è corretta (e bene su di loro per fare quello che era evidentemente un sacco di spec scavo!), Ma ho pensato di aggiungere un po 'di prospettiva storica.

Quando Mads ed io stavamo selezionando la formulazione esatta di varie parti della specifica per C# 3.0 ci siamo resi conto che il "tipo nullo" era bizzarro. È un "tipo" con un solo valore. È un "tipo" di cui Reflection non sa nulla. È un "tipo" che non ha un nome, che GetType non restituisce mai, che non è possibile specificare come tipo di una variabile o campo locale o altro.In breve, è davvero un "tipo" che serve solo a rendere il sistema di tipi "completo", in modo che ogni espressione in fase di compilazione abbia un tipo.

Tranne che C# aveva già espressioni prive di tipo: i gruppi di metodi in C# 1.0, i metodi anonimi in C# 2.0 e lambdas in C# 3.0 non hanno alcun tipo. Se tutte queste cose non possono avere alcun tipo, ci siamo resi conto che "null" non ha bisogno nemmeno di un tipo. Pertanto abbiamo rimosso i riferimenti al "tipo nullo" inutile in C# 3.0.

Come dettaglio di implementazione, le implementazioni Microsoft da C# 1.0 a 5.0 hanno tutti un oggetto interno per rappresentare il "tipo nullo". Hanno anche oggetti per rappresentare i tipi non esistenti di lambda, metodi anonimi e gruppi di metodi. Questa scelta di implementazione ha una serie di vantaggi e svantaggi. Dal lato pro, il compilatore può chiedere il tipo di qualsiasi espressione e ottenere una risposta. Da parte sua, ciò significa che a volte i bug nell'analisi del tipo che dovrebbero davvero aver bloccato il compilatore causano invece cambiamenti semantici nei programmi. Il mio esempio preferito è che in C# 2.0 è possibile utilizzare l'espressione illegale "null ?? null"; a causa di un bug, il compilatore non riesce a contrassegnarlo come un uso errato dell'operatore ?? e continua a dedurre che il tipo di questa espressione è "il tipo nullo", anche se non è un valore letterale nullo. Che poi continua a causare molti altri bug a valle mentre l'analizzatore di tipo cerca di dare un senso al tipo.

In Roslyn probabilmente non useremo questa strategia; piuttosto, semplicemente introdurremo nell'implementazione del compilatore che alcune espressioni non hanno alcun tipo.

+2

Speravo che Roslyn abilitasse a chiedere quale sia il tipo di qualsiasi espressione e aggiungerebbe definizioni di tipo per tutti quei tipi non di tipo. In quale altro modo si risponderebbe alla domanda "che tipo è l'espressione' null'? " – configurator

+0

@Eric: ma qual è la posizione ufficiale di Microsoft rispetto a? L'espressione 'null' ha un tipo? (Potrebbe non essere così sorprendente se non lo fosse, tenendo conto che lambda ecc. Non ha un tipo.) – Vlad

+6

@Vlad: la posizione ufficiale di C# 3.0 è che "null" non ha un tipo. –

0

Nonostante non abbia alcun tipo di runtime, null può essere convertito in un tipo in fase di compilazione, come illustrato in questo esempio.

In fase di esecuzione, è possibile trovare quella variabile stringAsObject detiene una string, non è solo un object, ma non è possibile trovare qualsiasi tipo per le variabili nullString e nullStringAsObject.

public enum Answer { Object, String, Int32, FileInfo }; 
private Answer GetAnswer(int i) { return Answer.Int32; } 
private Answer GetAnswer(string s) { return Answer.String; } 
private Answer GetAnswer(object o) { return Answer.Object; } 

[TestMethod] 
public void MusingAboutNullAtRuntimeVsCompileTime() 
{ 
    string nullString = null; 
    object nullStringAsObject = (string)null; 
    object stringAsObject = "a string"; 

    // resolved at runtime 
    Expect.Throws(typeof(ArgumentNullException),() => Type.GetTypeHandle(nullString)); 
    Expect.Throws(typeof(ArgumentNullException),() => Type.GetTypeHandle(nullStringAsObject)); 
    Assert.AreEqual(typeof(string), Type.GetTypeFromHandle(Type.GetTypeHandle(stringAsObject))); 
    Assert.AreEqual(typeof(string), stringAsObject.GetType()); 

    // resolved at compile time 
    Assert.AreEqual(Answer.String, this.GetAnswer(nullString)); 
    Assert.AreEqual(Answer.Object, this.GetAnswer(nullStringAsObject)); 
    Assert.AreEqual(Answer.Object, this.GetAnswer(stringAsObject)); 
    Assert.AreEqual(Answer.Object, this.GetAnswer((object)null)); 
    Assert.AreEqual(Answer.String, this.GetAnswer((string)null)); 
    Assert.AreEqual(Answer.String, this.GetAnswer(null)); 
} 

// Uncommenting the following method overload 
// makes the last statement in the test case ambiguous to the compiler 
// private Answer GetAnswer(FileInfo f) { return Answer.FileInfo; } 
Problemi correlati