Déclarer un tableau const

458

Est-il possible d'écrire quelque chose de semblable au suivant?

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Jaime Oro
la source
statique peut être utilisé, public static string [] Titles = new string [] {"German", "Spanish"};
Ray

Réponses:

691

Oui, mais vous devez le déclarer readonlyau lieu de const:

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

La raison en est qu'elle constne peut être appliquée qu'à un champ dont la valeur est connue au moment de la compilation. L'initialiseur de tableau que vous avez montré n'est pas une expression constante en C #, il génère donc une erreur de compilation.

La déclarer readonlyrésout ce problème car la valeur n'est initialisée qu'au moment de l'exécution (bien qu'elle soit garantie d'avoir été initialisée avant la première utilisation du tableau).

Selon ce que vous voulez finalement réaliser, vous pouvez également envisager de déclarer une énumération:

public enum Titles { German, Spanish, Corrects, Wrongs };
Cody Gray
la source
115
Notez que le tableau ici n'est pas en lecture seule, bien sûr; Titres [2] = "Gallois"; fonctionnerait très bien à l'exécution
Marc Gravell
46
Vous le voulez probablement aussi statique
tymtam
4
que diriez-vous de déclarer un tableau "const" dans un corps de méthode, pas dans celui de classe?
serhio
19
Désolé pour le vote négatif, mais const implique également statique. Déclarer le tableau en lecture seule n'est pas proche d'une solution de contournement. cela doit être readonly staticd'avoir une ressemblance avec la sémantique demandée.
Anton
3
@Anton, vous et vos "followers" avez-vous supprimé les votes négatifs? Pour moi, il staticn'est pas nécessaire de le faire fonctionner, il ajoute simplement la possibilité de référencer Titlessans avoir d'instance, mais supprime la possibilité de changer la valeur pour différentes instances (par exemple, vous pouvez avoir un constructeur avec un paramètre en fonction de la modification de la valeur de ce readonlychamp).
Sinatr
57

Vous pouvez déclarer un tableau en tant que readonly, mais gardez à l'esprit que vous pouvez modifier l'élément du readonlytableau.

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

Envisagez d'utiliser enum, comme l'a suggéré Cody, ou IList.

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();
Branimir
la source
31
Dans .NET 4.5 et versions ultérieures, vous pouvez déclarer une liste en tant que IReadOnlyList <string> au lieu d'IList <string>.
Grzegorz Smulko
Juste pour être clair, vous pouvez toujours changer les valeurs dans un IReadOnlyList (juste pas ajouter ou supprimer des éléments). Mais oui, le déclarer comme un IReadOnlyList serait mieux qu'un IList.
KevinVictor
La question concerne constNOT readonly...
Yousha Aleayoub
51

Vous ne pouvez pas créer un tableau «const» car les tableaux sont des objets et ne peuvent être créés qu'au moment de l'exécution et les entités const sont résolues au moment de la compilation.

Ce que vous pouvez faire à la place est de déclarer votre tableau comme "en lecture seule". Cela a le même effet que const, sauf que la valeur peut être définie au moment de l'exécution. Il ne peut être défini qu'une seule fois et est ensuite une valeur en lecture seule (c'est-à-dire const).

JAiro
la source
2
Cet article souligne pourquoi les tableaux ne peuvent pas être déclarés comme constantes
Radderz
1
Il est possible de déclarer un tableau constant; le problème l'initialise avec une valeur constante. Le seul exemple de travail qui me vient à l'esprit est celui const int[] a = null;qui n'est pas très utile, mais en fait une instance d'une constante de tableau.
waldrumpus
CECI est la seule bonne réponse.
Yousha Aleayoub
17

Depuis C # 6, vous pouvez l'écrire comme:

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

Voir aussi: C #: le nouveau et amélioré C # 6.0 (en particulier le chapitre "Fonctions et propriétés du corps d'expression")

Cela créera une propriété statique en lecture seule, mais vous permettra toujours de modifier le contenu du tableau renvoyé, mais lorsque vous appellerez à nouveau la propriété, vous obtiendrez à nouveau le tableau d'origine non modifié.

Pour plus de précision, ce code est le même que (ou en fait un raccourci pour):

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

Veuillez noter qu'il y a un inconvénient à cette approche: un nouveau tableau est en fait instancié sur chaque référence, donc si vous utilisez un très grand tableau, ce n'est peut-être pas la solution la plus efficace. Mais si vous réutilisez le même tableau (en le plaçant dans un attribut privé par exemple), cela ouvrira à nouveau la possibilité de modifier le contenu du tableau.

Si vous souhaitez avoir un tableau (ou une liste) immuable, vous pouvez également utiliser:

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

Mais, cela présente toujours un risque de modifications, car vous pouvez toujours le restituer dans une chaîne [] et modifier le contenu, en tant que tel:

((string[]) Titles)[1] = "French";
mjepson
la source
1
Quel est le bénéfice de l'utilisation de la propriété au lieu du champ dans ce cas?
nicolay.anykienko
2
Un champ ne peut pas renvoyer un nouvel objet à chaque appel. Une propriété est fondamentalement une sorte de "fonction déguisée".
mjepson
1
Si vous faisiez référence à la dernière option, cela peut être fait en utilisant à la fois un champ ou une propriété, mais comme elle est publique, je préfère une propriété. Je n'ai jamais utilisé de domaine public depuis l'introduction de Propriétés.
mjepson
1
Il y a un autre inconvénient à la première approche: vous n'obtiendrez pas d'erreur de compilation si vous l'assignez Titles[0], par exemple - en fait, la tentative d'assignation est discrètement ignorée. Combiné à l'inefficacité de recréer le tableau à chaque fois, je me demande si cette approche mérite même d'être montrée. En revanche, la 2ème approche est efficace, et vous devez tout faire pour vaincre l'immuabilité.
mklement0
6

Si vous déclarez un tableau derrière une interface IReadOnlyList, vous obtenez un tableau constant avec des valeurs constantes qui est déclaré lors de l'exécution:

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

Disponible dans .NET 4.5 et supérieur.

Richard Garside
la source
6

Une solution .NET Framework v4.5 + qui améliore la réponse de tdbeckett :

using System.Collections.ObjectModel;

// ...

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

Remarque: Étant donné que la collection est conceptuellement constante, il peut être judicieux staticde la déclarer au niveau de la classe .

Ce qui précède:

  • Initialise une fois le champ de support implicite de la propriété avec le tableau.

    • Notez que { get; }- c'est-à-dire, déclarer uniquement un getter de propriété - est ce qui rend la propriété elle-même implicitement en lecture seule (essayer de combiner readonlyavec { get; }est en fait une erreur de syntaxe).

    • Alternativement, vous pouvez simplement omettre le { get; }et ajouter readonlypour créer un champ au lieu d'une propriété, comme dans la question, mais exposer les membres de données publiques en tant que propriétés plutôt que champs est une bonne habitude à former.

  • Crée une structure de type tableau (permettant un accès indexé ) qui est vraiment et robuste en lecture seule (conceptuellement constante, une fois créée), à ​​la fois en ce qui concerne:

    • empêcher la modification de la collection dans son ensemble (par exemple en supprimant ou en ajoutant des éléments, ou en affectant une nouvelle collection à la variable).
    • empêcher la modification d' éléments individuels .
      (Même une modification indirecte n'est pas possible - contrairement à une IReadOnlyList<T>solution, où une (string[])distribution peut être utilisée pour accéder en écriture aux éléments , comme le montre la réponse utile de mjepsen .
      La même vulnérabilité s'applique à l' IReadOnlyCollection<T> interface , qui, malgré la similitude de nom à la classe ReadOnlyCollection , ne prend même pas en charge l' accès indexé , ce qui le rend fondamentalement inapproprié pour fournir un accès de type tableau.)
mklement0
la source
1
@mortb: Malheureusement, IReadOnlyCollectionne prend pas en charge l'accès indexé, il ne peut donc pas être utilisé ici. De plus, comme IReadOnlyList(qui a un accès indexé), il est susceptible d'être manipulé par un élément en le convertissant string[]. En d'autres termes: ReadOnlyCollection(dans lequel vous ne pouvez pas convertir un tableau de chaînes) est la solution la plus robuste. Ne pas utiliser de getter est une option (et j'ai mis à jour la réponse pour le noter), mais avec des données publiques , il est probablement préférable de s'en tenir à une propriété.
mklement0
5

Pour mes besoins, je définis un statictableau, au lieu d'impossible constet cela fonctionne: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

ALZ
la source
1
Le simple fait de supprimer constde l'exemple OP fonctionne également, mais cela (ou votre réponse) permet de changer à la fois: Titlesinstance et n'importe quelle valeur. Quel est donc l'intérêt de cette réponse?
Sinatr
@Sinatr, j'ai répondu à cela il y a 3 ans, quand je me suis mis à travailler en C #. Je l'ai laissé, maintenant je suis dans le monde Java. J'ai peut-être oublié d'ajouterreadonly
ALZ
Après réflexion, votre réponse est une façon directe de faire fonctionner le code OP , sans aucune considération const/ readonly, simplement en le faisant fonctionner (comme si constc'était une erreur de syntaxe). Pour certaines personnes, cela semble être une réponse valable (peut-être ont-ils également essayé d'utiliser constpar erreur?).
Sinatr
5

Vous pouvez adopter une approche différente: définir une chaîne constante pour représenter votre tableau, puis diviser la chaîne en un tableau lorsque vous en avez besoin, par exemple

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

Cette approche vous donne une constante qui peut être stockée dans la configuration et convertie en tableau si nécessaire.

Alastair
la source
8
Je pense que le coût de l'exécution de la division dépasse de loin tous les avantages apportés par la définition de const. Mais +1 pour une approche unique et sortir des sentiers battus! ;)
Radderz
J'étais sur le point de publier la même solution, puis j'ai vu cela, contrairement aux remarques dures et négatives, c'était en fait parfait pour mon scénario, où je devais passer un const à un attribut, puis j'ai divisé la valeur du constructeur d'attribut en obtenir ce dont j'avais besoin. et je ne vois aucune raison pour laquelle cela aura un coût de performance car les attributs ne sont pas créés pour chaque instance.
Kalpesh Popat
4

Dans un souci d'exhaustivité, nous avons également à notre disposition des tableaux immuables. Cela devrait être vraiment immuable:

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

Nécessite une référence NuGet System.Collections.Immutable

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

shurik
la source
3

C'est une façon de faire ce que vous voulez:

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

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

C'est très similaire à faire un tableau en lecture seule.

tdbeckett
la source
8
Vous pouvez simplement faire cela comme public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; pas besoin de recréer la liste à chaque récupération si vous en faites quand même un ReadOnlyCollection.
Nyerguds
3

Ceci est la seule bonne réponse. Vous ne pouvez pas actuellement faire cela.

Toutes les autres réponses suggèrent d'utiliser des variables statiques en lecture seule qui sont similaires , mais pas identiques, à une constante. Une constante est codée en dur dans l'assemblage. Une variable statique en lecture seule peut être définie une fois, probablement lors de l'initialisation d'un objet.

Ceux-ci sont parfois interchangeables, mais pas toujours.

Roger Hill
la source
2

Je crois que vous ne pouvez le faire qu'en lecture seule.

skaz
la source
2

Les tableaux sont probablement l'une de ces choses qui ne peuvent être évaluées qu'au moment de l'exécution. Les constantes doivent être évaluées au moment de la compilation. Essayez d'utiliser "en lecture seule" au lieu de "const".

nemke
la source
0

Comme alternative, pour contourner le problème des éléments pouvant être modifiés avec un tableau en lecture seule, vous pouvez utiliser une propriété statique à la place. (Les éléments individuels peuvent toujours être modifiés, mais ces modifications ne seront effectuées que sur la copie locale du tableau.)

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

Bien sûr, cela ne sera pas particulièrement efficace car un nouveau tableau de chaînes est créé à chaque fois.

Clapier
la source
0

Meilleure alternative:

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 };
Herman Schoenfeld
la source