Deklarieren Sie ein const-Array

458

Ist es möglich, etwas Ähnliches wie das Folgende zu schreiben?

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Jaime Oro
quelle
statisch könnte verwendet werden, öffentliche statische Zeichenfolge [] Titel = neue Zeichenfolge [] {"Deutsch", "Spanisch"};
Ray

Antworten:

691

Ja, aber Sie müssen es deklarieren readonlyanstatt const:

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

Der Grund ist, dass const nur auf ein Feld angewendet werden kann, dessen Wert zur Kompilierungszeit bekannt ist. Der angezeigte Array-Initialisierer ist kein konstanter Ausdruck in C #, daher wird ein Compilerfehler erzeugt.

Das Deklarieren readonlylöst dieses Problem, da der Wert erst zur Laufzeit initialisiert wird (obwohl er garantiert vor der ersten Verwendung des Arrays initialisiert wurde).

Je nachdem, was Sie letztendlich erreichen möchten, können Sie auch eine Aufzählung deklarieren:

public enum Titles { German, Spanish, Corrects, Wrongs };
Cody Grey
quelle
115
Beachten Sie, dass das Array hier natürlich nicht schreibgeschützt ist. Titel [2] = "Walisisch"; würde zur Laufzeit gut
funktionieren
46
Sie wollen es wahrscheinlich auch statisch
Tymtam
4
Wie wäre es, ein "const" -Array in einem Methodenkörper zu deklarieren, nicht in der Klasse eins?
Serhio
19
Entschuldigung für die Abstimmung, aber const impliziert auch statische Aufladung. Das Deklarieren des Arrays als schreibgeschützt kommt einer Problemumgehung nicht nahe. Es muss readonly staticÄhnlichkeit mit der angeforderten Semantik haben.
Anton
3
@Anton, haben Sie und Ihre "Follower" die Downvote entfernt? Für mich staticist es nicht erforderlich, damit es funktioniert. Es wird lediglich die Möglichkeit hinzugefügt, Titlesohne Instanz zu referenzieren , aber die Möglichkeit zu entfernen, den Wert für verschiedene Instanzen zu ändern (z. B. können Sie einen Konstruktor mit Parametern haben, je nachdem, welchen Wert Sie für dieses readonlyFeld ändern ).
Sinatr
57

Sie können das Array als deklarieren readonly, aber denken Sie daran, dass Sie das Element des readonlyArrays ändern können .

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

Erwägen Sie die Verwendung von enum, wie Cody vorgeschlagen hat, oder IList.

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();
Branimir
quelle
31
In .NET 4.5 und höher können Sie eine Liste als IReadOnlyList <string> anstelle von IList <string> deklarieren.
Grzegorz Smulko
Um es klar zu machen, können Sie die Werte in einer IReadOnlyList trotzdem ändern (nur keine Elemente hinzufügen oder entfernen). Aber ja, es wäre besser als eine IList, es als IReadOnlyList zu deklarieren.
KevinVictor
Die Frage ist über constNICHT readonly...
Yousha Aleayoub
51

Sie können kein 'const'-Array erstellen, da Arrays Objekte sind und nur zur Laufzeit erstellt werden können und const-Entitäten zur Kompilierungszeit aufgelöst werden.

Sie können stattdessen Ihr Array als "schreibgeschützt" deklarieren. Dies hat den gleichen Effekt wie const, außer dass der Wert zur Laufzeit festgelegt werden kann. Es kann nur einmal eingestellt werden und ist danach ein schreibgeschützter (dh konstanter) Wert.

JAiro
quelle
2
Dieser Beitrag hebt hervor, warum Arrays nicht als Konstanten deklariert werden können
Radderz
1
Es ist möglich, ein konstantes Array zu deklarieren. Das Problem besteht darin, es mit einem konstanten Wert zu initialisieren. Das einzige funktionierende Beispiel, das mir in den Sinn kommt, ist const int[] a = null;das nicht sehr nützliche, aber tatsächlich eine Instanz einer Array-Konstante.
Waldrumpus
Dies ist die einzig richtige Antwort.
Yousha Aleayoub
17

Seit C # 6 können Sie es wie folgt schreiben:

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

Siehe auch: C #: Das neue und verbesserte C # 6.0 (insbesondere das Kapitel "Funktionen und Eigenschaften des Ausdrucks")

Dadurch wird eine schreibgeschützte statische Eigenschaft erstellt, Sie können jedoch weiterhin den Inhalt des zurückgegebenen Arrays ändern. Wenn Sie die Eigenschaft jedoch erneut aufrufen, erhalten Sie das ursprüngliche, unveränderte Array erneut.

Zur Verdeutlichung ist dieser Code derselbe wie (oder eigentlich eine Abkürzung für):

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

Bitte beachten Sie, dass dieser Ansatz einen Nachteil hat: Bei jeder Referenz wird tatsächlich ein neues Array instanziiert. Wenn Sie also ein sehr großes Array verwenden, ist dies möglicherweise nicht die effizienteste Lösung. Wenn Sie jedoch dasselbe Array erneut verwenden (indem Sie es beispielsweise in ein privates Attribut einfügen), wird erneut die Möglichkeit eröffnet, den Inhalt des Arrays zu ändern.

Wenn Sie ein unveränderliches Array (oder eine Liste) haben möchten, können Sie auch Folgendes verwenden:

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

Dies birgt jedoch immer noch das Risiko von Änderungen, da Sie es immer noch in einen String [] umwandeln und den Inhalt als solchen ändern können:

((string[]) Titles)[1] = "French";
mjepson
quelle
1
Was ist in diesem Fall der Gewinn der Nutzung von Immobilien anstelle von Feldern?
nicolay.anykienko
2
Ein Feld kann nicht bei jedem Aufruf ein neues Objekt zurückgeben. Eine Eigenschaft ist im Grunde eine Art "Funktion in Verkleidung".
Mjepson
1
Wenn Sie sich auf die letzte Option bezogen haben, kann dies sowohl über ein Feld als auch über eine Eigenschaft erfolgen. Da diese jedoch öffentlich ist, bevorzuge ich eine Eigenschaft. Ich benutze seit Einführung von Properties kein öffentliches Feld mehr.
Mjepson
1
Der erste Ansatz hat noch einen weiteren Nachteil: Sie erhalten keinen Kompilierungsfehler, wenn Sie beispielsweise zuweisen. Titles[0]Tatsächlich wird der Zuweisungsversuch stillschweigend ignoriert. In Kombination mit der Ineffizienz, das Array jedes Mal neu zu erstellen, frage ich mich, ob dieser Ansatz überhaupt eine Darstellung wert ist. Im Gegensatz dazu ist der zweite Ansatz effizient und Sie müssen alles tun, um die Unveränderlichkeit zu besiegen.
mklement0
6

Wenn Sie ein Array hinter einer IReadOnlyList-Schnittstelle deklarieren, erhalten Sie ein konstantes Array mit konstanten Werten, das zur Laufzeit deklariert wird:

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

Verfügbar in .NET 4.5 und höher.

Richard Garside
quelle
6

Eine .NET Framework v4.5 + -Lösung , die die Antwort von tdbeckett verbessert :

using System.Collections.ObjectModel;

// ...

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

Hinweis: Da die Sammlung konzeptionell konstant ist, kann es sinnvoll sein static, sie in der Klasse zu deklarieren .

Obenstehendes:

  • Initialisiert das implizite Sicherungsfeld der Eigenschaft einmal mit dem Array.

    • Beachten Sie, dass { get; }- das heißt, erklärt nur eine Eigenschaft Getter - ist das, was die Eigenschaft macht sich implizit nur schreib (versucht zu kombinieren readonlymit { get; }ist eigentlich ein Syntaxfehler).

    • Alternativ können Sie das Feld weglassen { get; }und hinzufügen readonly, um ein Feld anstelle einer Eigenschaft zu erstellen , wie in der Frage beschrieben. Es ist jedoch eine gute Angewohnheit , öffentliche Datenelemente als Eigenschaften und nicht als Felder bereitzustellen.

  • Erstellt eine Array- ähnliche Struktur (die einen indizierten Zugriff ermöglicht ), die wirklich und robust schreibgeschützt ist (konzeptionell konstant, sobald sie erstellt wurde), sowohl in Bezug auf:

    • Verhindern der Änderung der gesamten Sammlung (z. B. durch Entfernen oder Hinzufügen von Elementen oder durch Zuweisen einer neuen Sammlung zur Variablen).
    • Verhinderung der Veränderung einzelner Elemente .
      (Auch indirekte Änderung nicht möglich ist - im Gegensatz zu einer IReadOnlyList<T>Lösung, bei der ein (string[])Guss verwendet werden kann , Schreibzugriff auf die Elemente zu gewinnen , wie es in gezeigt mjepsen die hilfreiche Antwort .
      Die gleiche Verwundbarkeit gilt für die IReadOnlyCollection<T> Schnittstelle , die trotz der Namensähnlichkeit zu Klasse ReadOnlyCollection , nicht unterstützt sogar indexierten Zugang , es grundsätzlich nicht geeignet für die Bereitstellung von Array-ähnlichem Zugriff zu machen.)
mklement0
quelle
1
@mortb: IReadOnlyCollectionUnterstützt leider keinen indizierten Zugriff, daher kann er hier nicht verwendet werden. Darüber hinaus ist es wie IReadOnlyList(das über einen indizierten Zugriff verfügt) anfällig für Elementmanipulationen durch Zurücksetzen auf string[]. Mit anderen Worten: ReadOnlyCollection(in die Sie kein String-Array umwandeln können) ist die robusteste Lösung. Es ist eine Option, keinen Getter zu verwenden (und ich habe die Antwort aktualisiert, um dies zu beachten), aber bei öffentlichen Daten ist es wahrscheinlich besser, bei einer Eigenschaft zu bleiben.
mklement0
5

Für meine Bedürfnisse definiere ich staticArray, anstatt unmöglich constund es funktioniert: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

ALZ
quelle
1
Das einfache Entfernen constaus dem OP-Beispiel funktioniert auch, aber das (oder Ihre Antwort) ermöglicht es, sowohl die TitlesInstanz als auch einen beliebigen Wert zu ändern . Worum geht es in dieser Antwort?
Sinatr
@ Sinatr, ich habe dies vor 3 Jahren beantwortet, als ich in C # gearbeitet habe. Ich habe es verlassen, jetzt bin ich in der Java-Welt. Vielleicht habe ich vergessen hinzuzufügenreadonly
ALZ
Nach reiflicher Überlegung ist die Antwort eine direkte , wie OP - Code Arbeits machen , ohne const/ readonlyÜberlegungen, einfach machen es funktioniert (wie wenn constwar ein Syntaxfehler). Für manche Menschen scheint es eine wertvolle Antwort zu sein (vielleicht haben sie auch versucht, sie versehentlich zu verwenden const?).
Sinatr
5

Sie können einen anderen Ansatz wählen: Definieren Sie eine konstante Zeichenfolge, um Ihr Array darzustellen, und teilen Sie die Zeichenfolge dann bei Bedarf in ein Array auf, z

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

Mit diesem Ansatz erhalten Sie eine Konstante, die in der Konfiguration gespeichert und bei Bedarf in ein Array konvertiert werden kann.

Alastair
quelle
8
Ich denke, die Kosten für die Durchführung des Split übersteigen bei weitem alle Vorteile, die durch die Definition von const erzielt werden. Aber +1 für einen einzigartigen Ansatz und Denken über den Tellerrand hinaus! ;)
Radderz
Ich wollte die gleiche Lösung veröffentlichen und sah dann, dass dies entgegen den harten und negativen Bemerkungen tatsächlich perfekt für mein Szenario war, in dem ich eine Konstante an ein Attribut übergeben musste und dann den Wert im Attributkonstruktor aufteilte Holen Sie sich, was ich brauchte. und ich sehe keinen Grund, warum es Leistungskosten verursachen wird, da Attribute nicht für jede Instanz erstellt werden.
Kalpesh Popat
4

Der Vollständigkeit halber verfügen wir jetzt auch über ImmutableArrays. Dies sollte wirklich unveränderlich sein:

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

Benötigt System.Collections.Immutable NuGet-Referenz

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

Shurik
quelle
3

So können Sie tun, was Sie wollen:

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

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

Es ist sehr ähnlich zu einem schreibgeschützten Array.

tdbeckett
quelle
8
Sie können das einfach so machen public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; Sie müssen die Liste nicht bei jedem Abruf neu erstellen, wenn Sie sie trotzdem zu einer ReadOnlyCollection machen.
Nyerguds
3

Dies ist die einzig richtige Antwort. Sie können dies derzeit nicht tun.

Alle anderen Antworten schlagen vor, statische schreibgeschützte Variablen zu verwenden, die ähnlich sind , die einer Konstanten , aber nicht identisch sind. Eine Konstante ist in der Baugruppe fest codiert. Eine statische schreibgeschützte Variable kann einmal eingestellt werden, wahrscheinlich wenn ein Objekt initialisiert wird.

Diese sind manchmal austauschbar, aber nicht immer.

Roger Hill
quelle
2

Ich glaube, Sie können es nur schreibgeschützt machen.

skaz
quelle
2

Arrays sind wahrscheinlich eines der Dinge, die nur zur Laufzeit ausgewertet werden können. Konstanten müssen zur Kompilierungszeit ausgewertet werden. Versuchen Sie, "readonly" anstelle von "const" zu verwenden.

nemke
quelle
0

Alternativ können Sie stattdessen eine statische Eigenschaft verwenden, um das Problem zu umgehen, dass Elemente mit einem schreibgeschützten Array geändert werden können. (Die einzelnen Elemente können weiterhin geändert werden, diese Änderungen werden jedoch nur auf der lokalen Kopie des Arrays vorgenommen.)

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

Dies ist natürlich nicht besonders effizient, da jedes Mal ein neues String-Array erstellt wird.

Stall
quelle
0

Beste 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 Schönfeld
quelle