Non è possibile utilizzare un array "inline" in C #?


89

Immagina di avere questo da qualche parte

public static T AnyOne<T>(this T[] ra) where T:class
    {
    int k = ra.Length;
    int r = Random.Range(0,k);
    return ra[r];
    }

o anche solo questo

public static string OneOf(this string[] strings)
    {
    return "a";
    }

Quindi, ovviamente puoi farlo ...

string[] st = {"a","b","c"};
string letter = st.AnyOne();

... che è grandioso. MA. Sembra che NON puoi farlo:

string letter = {"a","b","c"}.AnyOne();

o forse forse questo

string letter = ( {"a","b","c"} ).AnyOne();

o qualsiasi altra cosa che ho provato.

Infatti (1) perché non si può farlo? e (2) mi manca qualcosa, come lo faresti se ci fosse un modo?


5
Non sono sicuro che la domanda duplicata sia appropriata, l'OP non chiede informazioni sugli inizializzatori di array, ma perché il compilatore non riconoscerà l'oggetto come array finché non viene assegnato.
Ron Beyer

4
Non ho familiarità con la terminologia C #, ma credo che sia più comunemente chiamato letterale o letterale array piuttosto che inline .
chi

3
Quell'elemento sintattico è un inizializzatore di array o di raccolta , a seconda del contesto in cui viene utilizzato. In nessuno dei due casi è classificato come espressione .
Eric Lippert

Risposte:


129

Devi prima creare l'array, usando new[].

string letter = (new[] {"a","b","c"}).AnyOne();

Come menzionato da @hvd, puoi farlo senza parentesi (..), ho aggiunto le parentesi perché penso che sia più leggibile.

string letter = new[] {"a","b","c"}.AnyOne();

E puoi specificare il tipo di dati new string[]come è stato menzionato in altre risposte.


Non puoi semplicemente farlo {"a","b","c"}, perché puoi pensarlo come un modo per popolare l'array, non per crearlo.

Un altro motivo sarà che il compilatore sarà confuso, non saprà cosa creare, ad esempio a string[]{ .. }o a List<string>{ .. }.

Usando solo il new[]compilatore puoi sapere per tipo di dati ( ".."), tra {..}, cosa vuoi ( string). La parte essenziale è []che significa che vuoi un array.

Non puoi nemmeno creare un array vuoto con new[].

string[] array = new []{ }; // Error: No best type found for implicity-typed array

13
Non hai bisogno di quelle parentesi. string letter = new[] {"a","b","c"}.AnyOne();va bene. Se li vuoi, se pensi che sia più leggibile con le parentesi, sono validi, ma in quel caso penso che valga almeno la pena ricordare che è una tua scelta consapevole, che non è stata forzata dal linguaggio.

Conosco la nuova sintassi [] {1,2}, ma esiste una sintassi ancora più semplice? Qualcosa come [1, 2]?
seguso

51

(1) perché non si può farlo? {"a","b","c"}.AnyOne();

Questa linea:

string[] st = {"a","b","c"};

è una scorciatoia per un'espressione di creazione di array equivalente (sotto ILSpy )

string[] st = new string[]  {"a","b","c"};

Questo string[] st = {"a","b","c"} può essere utilizzato solo al momento della dichiarazione , non puoi non usarlo altrove, non puoi nemmeno fare:

string[] st;
st = {"a", "b", "c"}; //Error

È spiegato nella Sezione 7.6.10.4 per l'espressione di creazione di array nelle specifiche del linguaggio C #.

Quindi questo "{"a", "b", "c"}"da solo senza l'uso nella dichiarazione non significa nulla. Quindi non puoi usarlo con il tuo metodo di estensione, poiché il tuo metodo di estensione opera su un array.

(2) mi sto perdendo qualcosa, come lo faresti se ci fosse un modo?

Già menzionato nella risposta di @ adricadar , puoi fare:

(new[] {"a","b","c"}).AnyOne();

o

(new string[] {"a","b","c"}).AnyOne();

48

Respingo le domande "perché no" perché in primo luogo, le risposte non sono quasi mai soddisfacenti - hai già ottenuto la risposta "la funzione non è come la desideri perché la specifica non dice quello che vuoi che dica" , che immagino non sia stata una risposta particolarmente soddisfacente. In secondo luogo, il team di progettazione non deve giustificare il motivo per cui il mondo non è come vorresti che fosse; le funzionalità non esistono gratuitamente e sono quindi progettate fuori dal linguaggio; piuttosto, le caratteristiche devono essere prima giustificate e poi progettate in.

Quindi proviamo a rendere la tua domanda "perché no" un po 'più chiara. La funzionalità esistente è "un inizializzatore di array può essere utilizzato (a) sul lato destro degli uguali in un'inizializzazione o (b) a destra della costruzione di un oggetto di tipo array". La caratteristica proposta è: "un inizializzatore di array può anche essere usato come espressione". La domanda è: "quali critiche farebbe Eric alla funzione proposta?"

La prima critica che vorrei fare è che non è chiaro quale sia il tipo di espressione. In un inizializzatore di variabili hai il tipo della variabile e in un'espressione di creazione di oggetti hai il tipo di oggetto; da entrambi possiamo dedurre il tipo di array costruito. Senza alcun accenno, che tipo dovremmo dedurre?

In C # 1.0, quando è stata aggiunta questa funzionalità, era presente un totale complessivo di zero inferenze di tipo effettuate nel linguaggio. Un principio di progettazione nei primi giorni di C # era "nessuna sorpresa" e che il compilatore non era "troppo intelligente". Se lo sviluppatore intende che un'espressione sia di un tipo particolare, quel tipo dovrebbe essere in qualche modo ovvio nell'espressione. Quando dici

new double[] { 1, 2, 3.4 }

è abbastanza chiaro quale tipo si intende. Allo stesso modo

new Animal[] { cat, dog, null }

La funzione proposta viola questo principio. L'espressione deve avere un tipo, ma non è affatto chiaro in quale tipo si trovi l'argomento

M({cat, dog, null})

Inoltre: supponiamo di avere due overload di M, uno dei quali accetta un array di Animale uno che accetta un array di IPet. Di quale sovraccarico Mè applicabile? Una delle conversioni è migliore dell'altra? I tipi di elementi sono CateDog ; ha senso dedurre un tipo che non compare nemmeno lì? Sono tutte domande che devono essere prese in considerazione dal team di progettazione, e queste sono domande che non hanno risposte ovvie. La caratteristica proposta ci conduce in acque profonde in un ordine piuttosto breve.

Ora, C # 3.0 risolve questo problema perché C # 3.0 ha aggiunto numerose funzionalità in cui il compilatore deduce i tipi per conto dello sviluppatore. I principi precedenti su "nessuna sorpresa" e "regole semplici" erano in conflitto con altri principi di progettazione necessari per far funzionare LINQ. La funzionalità che proponi dovrebbe essere stata aggiunta in C # 3.0?

Avrebbe potuto essere. La funzionalità effettivamente aggiunta in C # 3.0 era:

new[] { x, y, z }

deduce il tipo dell'array usando l'algoritmo: prendi le espressioni per gli elementi che hanno tipi, determina quale di quei tipi è il tipo unico più generale in cui tutte le altre espressioni sono convertibili e, se esiste un tale tipo, scegli quello. Altrimenti produce un errore,

Quella caratteristica avrebbe potuto essere ulteriormente attenuata per rendere new[]opzionale. Questo non è stato fatto.

Ora, se mi avessi chiesto nel lasso di tempo di C # 3.0 di criticare la funzionalità proposta, avrei sottolineato che (1) il compilatore C # 3.0 era già in grave pericolo di slittare la pianificazione dell'intero rilascio, quindi non aggiungiamolo altro carico di progettazione, implementazione e test per una funzionalità completamente non necessaria che consente all'utente di risparmiare sei battiture e (2) C # 3.0 ha anche aggiunto inizializzatori di raccolta:

new List<int>() { 10, 20, 30 }

perché dovrebbe {10, 20, 30}essere automaticamente un array ? Perché non dovrebbe essere un List<int>? O uno qualsiasi di una serie di altri tipi? Perché il bias verso gli array? Ricorda, una volta che scegliamo di racchiudere la sintassi per gli array, siamo bloccati per sempre . Potrebbe non essere mai nient'altro, quindi la funzionalità proposta non solo non è necessaria, ma impedisce anche possibili funzionalità future che sembrano plausibili.

Riassumendo: la funzionalità proposta violava direttamente alcuni dei principi di progettazione di C # 1.0. Non aggiunge altro che un carico inutile a C # 3.0. In tutte le versioni del linguaggio a partire da C # 3.0, la funzionalità proposta non ha buoni argomenti per consigliare di spendere tempo, impegno e denaro su di essa rispetto a molte altre funzionalità più degne.

Pertanto, nessuna caratteristica del genere.


Eh, devi avere una maglietta con la scritta "il mondo non è come vuoi che sia" stampato su di essa :)
slugster

6
@ JoeBlow: Prima di tutto, sei il benvenuto. Riguardo al "perché no", il tuo commento illustra bene il problema. Quando alcune persone fanno una domanda sul "perché", cercano una giustificazione logica . Alcune persone cercano una giustificazione pragmatica . E a quanto pare stai cercando la riga della specifica che descrive la regola . È così vago che è molto difficile creare una buona risposta che indirizzi la domanda effettivamente nella mente dell'interrogante. Le domande del "perché no" sono anche peggiori perché sono domande vaghe su cose che nemmeno esistono .
Eric Lippert

3
@EricLippert È anche peggio della semplice domanda sulle cose che _potrebbero_ esistere : se stai lavorando su un software con un team, l'intero team trascorre ogni giorno dell'anno pensando alle funzionalità e bilanciando le conseguenze. Le decisioni vengono prese. Richieste di funzionalità e "perché" chiede una giustificazione. Tuttavia, perché non implica che qualcuno voglia solo fare qualcosa, il che fondamentalmente mette in dubbio il giudizio della squadra stessa. Peggio ancora, la persona che chiede il perché no di solito sa poco dell'argomento. In quanto tale, penso che respingere le domande sul perché no sia totalmente equo. Buon lavoro IMO.
atlante
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.