14

Ho sempre pensato che funzionasse bene in entrambi i modi. Poi ha fatto questo test e si rese conto che non è consentito su ri-assegnazioni:Perché non sono consentiti gli inizializzatori di raccolta su riassegnazioni?

int[] a = {0, 2, 4, 6, 8}; 

funziona bene, ma non:

int [ ] a; 
a = { 0, 2, 4, 6, 8 }; 

Qual è il motivo tecnico per questo? Ho pensato di chiederlo qui, perché questo comportamento era quello che mi aspettavo intuitivamente.

+4

È possibile trovare questo utile: http://stackoverflow.com/questions/7351453/why-cant-i-use-the-array-initializer-with-an-implicitly-typed-variable (non strettamente un duplicato) –

risposta

20

Prima di tutto, andiamo i termini corretti. Questo non è un inizializzatore di raccolta . Questo è un inizializzatore dell'array . Un inizializzatore di raccolta segue sempre un costruttore per un tipo di raccolta. Un inizializzatore di array è legale solo in un inizializzatore di dichiarazione locale o campo o in un'espressione di creazione di array.

Hai completamente ragione a notare che questa è una regola dispari. Permettetemi di caratterizzare la sua stranezza precisamente:

Supponiamo di avere un metodo M che prende una serie di ints. Tutti questi sono legali:

int[] x = new[] { 10, 20, 30 }; 
int[] y = new int[] { 10, 20, 30 }; 
int[] z = new int[3] { 10, 20, 30 }; 
M(new[] { 10, 20, 30 }); 
M(new int[] { 10, 20, 30 }); 
M(new int[3] { 10, 20, 30 }); 

Ma

int[] q = {10, 20, 30}; // legal! 
M({ 10, 20, 30 }); // illegal! 

Sembra che sia il "solitario" serie di inizializzazione dovrebbe essere legale in tutto il mondo che il "decorato" uno è, o da nessuna parte. È strano che ci sia questa pseudo-espressione valida solo in un inizializzatore, e non altrove che un'espressione è legale.

Prima di criticare e difendere entrambe questa scelta, voglio dire che prima di tutto, questa discrepanza è un incidente storico. Non c'è una ragione convincente per questo. Se potessimo sbarazzarcene senza infrangere il codice, lo faremmo. Ma non possiamo. Stavamo progettando C# da zero ancora oggi penso che le probabilità siano buone che l'inizializzatore di array "solitario" senza "nuovo" non sarebbe una sintassi valida.

Quindi, vorrei prima fornire alcuni motivi per cui gli inizializzatori di array NON devono essere consentiti come espressioni e devono essere consentiti negli inizializzatori di variabili locali. Poi darò alcune ragioni per il contrario.

Motivi per cui inizializzatori di array non dovrebbero essere consentite come espressioni:

inizializzatori Array violano la proprietà piacevole che { significa sempre introduzione di un nuovo blocco di codice. Il parser di ripristino degli errori nell'IDE che analizza mentre si digita mi piace utilizzare le parentesi come un modo conveniente per dire quando un'istruzione è incompleta; se si vede:

if (x == M(
{ 
    Q(

Poi è abbastanza facile per l'editor di codice di indovinare che ti manca )) prima della {. l'editor presumerà che Q( sia l'inizio di una dichiarazione e che manchi la sua fine.

Ma se inizializzatori di array sono espressioni legali allora potrebbe essere che ciò che manca è )})){}seguente il Q.

In secondo luogo, gli inizializzatori di array come espressioni violano il buon principio che tutte le allocazioni di heap hanno "nuovo" in loro da qualche parte.

Motivi per cui inizializzatori di array dovrebbero essere ammessi in campo e locali inizializzatori:

ricordare che inizializzatori di array sono state aggiunte alla lingua in v1.0, i locali prima che implicitamente tipizzate, tipi anonimi, o inferenza di tipo su array. Indietro nel giorno, non abbiamo avuto la piacevole "new [] {10, 20, 30}" sintassi, quindi senza inizializzatori di array che avrebbe dovuto dire:

int[] x = new int[] { 10, 20, 30 }; 

che sembra molto ridondante! Posso capire perché volevano ottenere quel "nuovo int []" fuori da lì.

Quando si dice

int[] x = { 10, 20, 30 }; 

non è sintatticamente ambigua; il parser sa che questo è un inizializzatore di array e non l'inizio di un blocco di codice (diversamente dal caso che ho menzionato sopra). Né è di tipo ambiguo; è chiaro che l'inizializzatore è una matrice di ints dal contesto.

Questo argomento giustifica il motivo per cui negli inizializzatori di array C# 1.0 erano consentiti in inizializzatori locali e di campo ma non in contesti di espressione.

Ma questo non è il mondo in cui ci troviamo oggi. Se stessimo progettando questo da zero oggi probabilmente non avremmo inizializzatori di array che non hanno "nuovo". Oggigiorno ovviamente ci rendiamo conto che la soluzione migliore è:

var x = new[] { 10, 20, 30 }; 

e tale espressione è valida in qualsiasi contesto. È possibile digitarlo esplicitamente sul lato "dichiarazione" o sul lato "inizializzatore" di = se lo ritieni opportuno, oppure puoi lasciare che il compilatore deduca i tipi di entrambi i lati o entrambi.

Quindi, riassumendo, sì, hai ragione che è incoerente che gli inizializzatori di array possano essere solo in dichiarazioni locali e di campo ma non in contesti di espressione. C'era una buona ragione per quello dieci anni fa, ma nel mondo moderno con inferenza di tipo, non c'è più una buona ragione per questo. È solo un incidente storico a questo punto.

+4

Wow. StackOverflow sarebbe 1000 volte più impressionante se avessi postato una risposta ad ogni domanda C#. –

+0

@JimSchubert: Apprezzo molto il sentimento, ma ti assicuro che sarebbe peggio. Non conosco la risposta al 99% delle domande C# sul sito! Sono un esperto di storia, progettazione e implementazione della lingua; concentrandoci strettamente su ciò negli ultimi sei anni significa che ci sono vaste aree di programmazione C# pratica di cui non so quasi nulla. –

+0

Ad ogni modo, adoro leggere le tue risposte! – Matthias

1

IT più essere:

int [] a;// ={1,2,3,4}; 
    a = new [] {1, 2, 3, 4}; 

In VB funziona allo stesso modo come dichiarazione, più facile xD

Dim a() As Integer '={1,2,3,4} 
a = {1, 2, 3, 4} 
+1

Chiede perché questa restrizione esiste. –

+0

Grazie, aggiungendo 'new []' prima, lo fa funzionare. Ma perché questa limitazione esiste quando potrebbe funzionare senza di essa (proprio come quando la assegni prima) –

+0

Beh, penso che sia solo per il linguaggio, perché in VB si può fare, dovremmo guardare il codice MSIL generato che cos'è la differenza . – Piyey