Dichiarare un array const


458

È possibile scrivere qualcosa di simile al seguente?

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

statico potrebbe essere usato, stringa statica pubblica [] Titoli = nuova stringa [] {"Tedesco", "Spagnolo"};
Ray,

Risposte:


691

Sì, ma devi dichiararlo readonlyinvece di const:

public static readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

Il motivo è che constpuò essere applicato solo a un campo il cui valore è noto in fase di compilazione. L'inizializzatore di array che hai mostrato non è un'espressione costante in C #, quindi produce un errore del compilatore.

La dichiarazione readonlyrisolve questo problema perché il valore non è inizializzato fino al runtime (sebbene sia garantito che sia stato inizializzato prima della prima volta che l'array viene utilizzato).

A seconda di ciò che alla fine vuoi raggiungere, potresti anche considerare di dichiarare un enum:

public enum Titles { German, Spanish, Corrects, Wrongs };

115
Si noti che l' array qui non è di sola lettura, ovviamente; Titoli [2] = "Welsh"; funzionerebbe benissimo in fase di esecuzione
Marc Gravell

46
Probabilmente lo vuoi anche statico
tymtam

4
che ne dici di dichiarare un array "const" in un corpo del metodo, non nella classe uno?
serhio,

19
Ci scusiamo per il voto negativo, ma const implica anche statico. Dichiarare l'array in sola lettura non è vicino a una soluzione alternativa. deve readonly staticavere qualsiasi somiglianza con la semantica richiesta.
Anton,

3
@Anton, tu e i tuoi "follower" avete rimosso il voto negativo? Per me staticnon è necessario per farlo funzionare, è sufficiente aggiungere la possibilità di fare riferimento Titlessenza avere istanza, ma rimuovere la possibilità di modificare il valore per diverse istanze (ad es. Si può avere un costruttore con parametro a seconda di quale si cambia valore di quel readonlycampo).
Sinatr,

57

Puoi dichiarare array come readonly, ma tieni presente che puoi cambiare elemento readonlydell'array.

public readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
...
Titles[0] = "bla";

Prendi in considerazione l'uso di enum, come suggeriva Cody, o IList.

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();

31
In .NET 4.5 e versioni successive è possibile dichiarare un elenco come IReadOnlyList <stringa> anziché IList <stringa>.
Grzegorz Smulko,

Per essere chiari, puoi comunque modificare i valori in un IReadOnlyList (semplicemente non aggiungere o rimuovere elementi). Ma sì, dichiararlo come IReadOnlyList sarebbe meglio di un IList.
KevinVictor

La domanda riguarda constNOT readonly...
Yousha Aleayoub il

51

Non è possibile creare un array 'const' perché gli array sono oggetti e possono essere creati solo in fase di esecuzione e le entità const vengono risolte al momento della compilazione.

Quello che puoi fare invece è dichiarare l'array come "di sola lettura". Ciò ha lo stesso effetto di const tranne che il valore può essere impostato in fase di esecuzione. Può essere impostato una sola volta ed è successivamente un valore di sola lettura (cioè const).


2
Questo post evidenzia perché le matrici non possono essere dichiarate come costanti
Radderz,

1
È possibile dichiarare un array costante; il problema lo sta inizializzando con un valore costante. L'unico esempio funzionante che viene in mente è const int[] a = null;che non è molto utile, ma in effetti un'istanza di una costante di array.
Waldrumpus,

Questa è l'unica risposta corretta.
Yousha Aleayoub,

17

Da C # 6 puoi scriverlo come:

public static string[] Titles => new string[] { "German", "Spanish", "Corrects", "Wrongs" };

Vedi anche: C #: Il nuovo e migliorato C # 6.0 (in particolare il capitolo "Funzioni e proprietà del corpo di espressione")

Ciò renderà una proprietà statica di sola lettura, ma consentirà comunque di modificare il contenuto dell'array restituito, ma quando si chiama di nuovo la proprietà, si otterrà nuovamente l'array originale e inalterato.

Per chiarimenti, questo codice è uguale a (o in realtà una scorciatoia per):

public static string[] Titles
{
    get { return new string[] { "German", "Spanish", "Corrects", "Wrongs" }; }
}

Si noti che c'è un aspetto negativo di questo approccio: un nuovo array è effettivamente istanziato su ogni riferimento, quindi se si utilizza un array molto grande, questa potrebbe non essere la soluzione più efficiente. Ma se riutilizzi lo stesso array (inserendolo ad esempio in un attributo privato), si aprirà nuovamente la possibilità di modificare il contenuto dell'array.

Se vuoi avere un array (o un elenco) immutabile, puoi anche usare:

public static IReadOnlyList<string> Titles { get; } = new string[] { "German", "Spanish", "Corrects", "Wrongs" };

Tuttavia, ciò comporta ancora il rischio di modifiche, in quanto è ancora possibile eseguirne il cast su una stringa [] e modificare il contenuto, in quanto tale:

((string[]) Titles)[1] = "French";

1
Qual è il profitto derivante dall'uso della proprietà anziché del campo in questo caso?
nicolay.anykienko,

2
Un campo non può restituire un nuovo oggetto ad ogni chiamata. Una proprietà è fondamentalmente una sorta di "funzione mascherata".
mjepson,

1
Se ti riferissi all'ultima opzione, che può essere fatta usando sia un campo che una proprietà, ma poiché è pubblica, preferisco una Proprietà. Non ho mai usato un campo pubblico dall'introduzione di Proprietà.
mjepson,

1
C'è un altro aspetto negativo del primo approccio: non otterrai un errore di compilazione se ti assegni Titles[0], ad esempio - in effetti, il tentativo di assegnazione viene tranquillamente ignorato. Combinato con l'inefficienza di ricreare l'array ogni volta, mi chiedo se valga la pena mostrare questo approccio. Al contrario, il secondo approccio è efficiente e devi fare di tutto per sconfiggere l'immutabilità.
mklement0

6

Se si dichiara un array dietro un'interfaccia IReadOnlyList, si ottiene un array costante con valori costanti dichiarati in fase di esecuzione:

public readonly IReadOnlyList<string> Titles = new [] {"German", "Spanish", "Corrects", "Wrongs" };

Disponibile in .NET 4.5 e versioni successive.


6

Una soluzione .NET Framework v4.5 + che migliora la risposta di tdbeckett :

using System.Collections.ObjectModel;

// ...

public ReadOnlyCollection<string> Titles { get; } = new ReadOnlyCollection<string>(
  new string[] { "German", "Spanish", "Corrects", "Wrongs" }
);

Nota: dato che la raccolta è concettualmente costante, può essere sensato farla staticdichiarare a livello di classe .

Quanto sopra:

  • Inizializza il campo di supporto implicito della proprietà una volta con l'array.

    • Si noti che { get; }- vale a dire, dichiarando solo una proprietà getter - è ciò che rende la struttura in sé implicitamente sola lettura (cercando di coniugare readonlycon { get; }è in realtà un errore di sintassi).

    • In alternativa, puoi semplicemente omettere { get; }e aggiungere readonlyper creare un campo anziché una proprietà, come nella domanda, ma esporre i membri di dati pubblici come proprietà anziché come campi è una buona abitudine da formare.

  • Crea una struttura simile a un array (che consente l' accesso indicizzato ) che è di sola lettura (in modo concettualmente costante, una volta creata), sia rispetto a:

    • impedire la modifica della raccolta nel suo insieme (ad esempio rimuovendo o aggiungendo elementi o assegnando una nuova raccolta alla variabile).
    • impedire la modifica di singoli elementi .
      (Anche indiretta modifica non è possibile - a differenza di una IReadOnlyList<T>soluzione, in cui un (string[])getto può essere utilizzata per ottenere l'accesso in scrittura agli elementi , come indicato nella risposta utile di mjepsen .
      La stessa vulnerabilità riguarda l' IReadOnlyCollection<T> interfaccia , che, nonostante la somiglianza nel nome alla classe ReadOnlyCollection , non supporta nemmeno l' accesso indicizzato , rendendolo fondamentalmente inadatto per fornire un accesso simile a array.)

1
@mortb: Sfortunatamente, IReadOnlyCollectionnon supporta l'accesso indicizzato, quindi non può essere utilizzato qui. Inoltre, come IReadOnlyList(che ha accesso indicizzato) è suscettibile alla manipolazione degli elementi eseguendo il cast su string[]. In altre parole: ReadOnlyCollection(su cui non è possibile eseguire il cast di un array di stringhe) è la soluzione più affidabile. Non usare un getter è un'opzione (e ho aggiornato la risposta per notare che), ma con i dati pubblici è probabilmente meglio attenersi a una proprietà.
mklement0

5

Per le mie esigenze, definisco staticarray, anziché impossibile conste funziona: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };


1
Anche la semplice rimozione constdall'esempio OP funziona, ma ciò (o la tua risposta) consente di modificare sia: Titlesistanza che qualsiasi valore. Quindi qual è il punto in questa risposta?
Sinatr,

@Sinatr, ho risposto a questo 3 anni fa, quando ho iniziato a lavorare in C #. L'ho lasciato, ora sono nel mondo Java. Forse ho dimenticato di aggiungerereadonly
ALZ

Dopo un secondo pensiero, la tua risposta è diretta su come far funzionare il codice OP , senza alcuna const/ readonlyconsiderazione, semplicemente facendolo funzionare (come se constfosse un errore di sintassi). Per alcune persone sembra essere una risposta preziosa (forse hanno anche provato a usare constper errore?).
Sinatr

5

Potresti adottare un approccio diverso: definire una stringa costante per rappresentare l'array e quindi dividere la stringa in un array quando ne hai bisogno, ad es.

const string DefaultDistances = "5,10,15,20,25,30,40,50";
public static readonly string[] distances = DefaultDistances.Split(',');

Questo approccio fornisce una costante che può essere memorizzata nella configurazione e convertita in un array quando necessario.


8
Penso che il costo dell'esecuzione della suddivisione superi di gran lunga qualsiasi vantaggio apportato dalla definizione di const. Ma +1 per un approccio unico e pensare fuori dagli schemi! ;)
Radderz,

Stavo per pubblicare la stessa soluzione e poi ho visto questo, contrariamente alle osservazioni dure e negative, questo era in realtà perfetto per il mio scenario, dove avevo bisogno di passare una const a un attributo, e quindi ho diviso il valore nel costruttore di attributi per ottieni quello di cui avevo bisogno. e non vedo alcun motivo per cui avrà un costo in termini di prestazioni poiché gli attributi non vengono creati per ogni istanza.
Kalpesh Popat,

4

Per completezza, ora abbiamo anche ImmutableArrays a nostra disposizione. Questo dovrebbe essere veramente immutabile:

public readonly static ImmutableArray<string> Tiles = ImmutableArray.Create(new[] { "German", "Spanish", "Corrects", "Wrongs" });

Richiede riferimento NuGet System.Collections.Immutable

https://msdn.microsoft.com/en-us/library/mt452182(v=vs.111).aspx


3

Questo è un modo per fare quello che vuoi:

using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

public ReadOnlyCollection<string> Titles { get { return new List<string> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();}}

È molto simile a fare un array di sola lettura.


8
Puoi farlo come public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; non è necessario ricreare l'elenco su ogni recupero se lo si rende comunque ReadOnlyCollection.
Nyerguds,

3

Questa è l'unica risposta corretta. Al momento non puoi farlo.

Tutte le altre risposte suggeriscono di utilizzare variabili di sola lettura statiche simili , ma non uguali a, una costante. Una costante è codificata nell'assieme. Una variabile di sola lettura statica può essere impostata una volta, probabilmente quando un oggetto viene inizializzato.

Questi sono talvolta intercambiabili, ma non sempre.


2

Credo che tu possa farcela in sola lettura.


2

Le matrici sono probabilmente una di quelle cose che possono essere valutate solo in fase di esecuzione. Le costanti devono essere valutate al momento della compilazione. Prova a utilizzare "sola lettura" anziché "const".


0

In alternativa, per aggirare il problema degli elementi che possono essere modificati con un array di sola lettura, è possibile utilizzare invece una proprietà statica. (I singoli elementi possono ancora essere modificati, ma queste modifiche verranno apportate solo sulla copia locale dell'array.)

public static string[] Titles 
{
    get
    {
        return new string[] { "German", "Spanish", "Corrects", "Wrongs"};
    }
}

Naturalmente, questo non sarà particolarmente efficiente poiché ogni volta viene creato un nuovo array di stringhe.


0

La migliore alternativa:

public static readonly byte[] ZeroHash = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
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.