Declarar una matriz constante

458

¿Es posible escribir algo similar a lo siguiente?

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Jaime Oro
fuente
static podría ser usado, public static string [] Titles = new string [] {"German", "Spanish"};
Ray

Respuestas:

691

Sí, pero debe declararlo en readonlylugar de const:

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

La razón es que const solo se puede aplicar a un campo cuyo valor se conoce en tiempo de compilación. El inicializador de matriz que ha mostrado no es una expresión constante en C #, por lo que produce un error de compilación.

Declarar que readonlyresuelve ese problema porque el valor no se inicializa hasta el tiempo de ejecución (aunque se garantiza que se ha inicializado antes de la primera vez que se usa la matriz).

Dependiendo de qué es lo que finalmente desea lograr, también puede considerar declarar una enumeración:

public enum Titles { German, Spanish, Corrects, Wrongs };
Cody Gray
fuente
115
Tenga en cuenta que la matriz aquí no es de solo lectura, por supuesto; Títulos [2] = "Galés"; funcionaría bien en tiempo de ejecución
Marc Gravell
46
Probablemente también lo quieras estático
tymtam
44
¿qué tal declarar una matriz "const" en un cuerpo de método, no en la clase uno?
serhio
19
Perdón por el voto negativo, pero const también implica estática. Declarar la matriz como solo lectura no está cerca de una solución alternativa. tiene que ser readonly staticpara tener algún parecido con la semántica solicitada.
Anton
3
@Anton, ¿usted y sus "seguidores" han eliminado el voto negativo? Para mí, staticno es necesario que funcione, solo agrega la posibilidad de hacer referencia Titlessin tener una instancia, pero elimina la posibilidad de cambiar el valor para diferentes instancias (por ejemplo, puede tener un constructor con un parámetro dependiendo de cuál cambie el valor de ese readonlycampo).
Sinatr
57

Puede declarar la matriz como readonly, pero tenga en cuenta que puede cambiar el elemento de la readonlymatriz.

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

Considere usar enum, como sugirió Cody, o IList.

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();
Branimir
fuente
31
En .NET 4.5 y superior puede declarar una lista como IReadOnlyList <string> en lugar de IList <string>.
Grzegorz Smulko
Para que quede claro, aún puede cambiar los valores en una IReadOnlyList (simplemente no agregar o eliminar elementos). Pero sí, declararlo como una IReadOnlyList sería mejor que una IList.
KevinVictor
La pregunta es sobre constNO readonly...
Yousha Aleayoub
51

No puede crear una matriz 'const' porque las matrices son objetos y solo se pueden crear en tiempo de ejecución y las entidades const se resuelven en tiempo de compilación.

Lo que puede hacer en su lugar es declarar su matriz como "solo lectura". Esto tiene el mismo efecto que const, excepto que el valor se puede establecer en tiempo de ejecución. Solo se puede configurar una vez y luego es un valor de solo lectura (es decir, constante).

JAiro
fuente
2
Esta publicación destaca por qué las matrices no pueden declararse como constantes
Radderz
1
Es posible declarar una matriz constante; El problema es inicializarlo con un valor constante. El único ejemplo de trabajo que viene a la mente es el const int[] a = null;que no es muy útil, pero sí una instancia de una constante de matriz.
waldrumpus
Esta es la única respuesta correcta.
Yousha Aleayoub
17

Desde C # 6 puedes escribirlo como:

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

Ver también: C #: El nuevo y mejorado C # 6.0 (específicamente el capítulo "Funciones y propiedades con cuerpo de expresión")

Esto creará una propiedad estática de solo lectura, pero aún así le permitirá alterar el contenido de la matriz devuelta, pero cuando vuelva a llamar a la propiedad, volverá a obtener la matriz original sin alterar.

Para aclarar, este código es el mismo que (o en realidad es una abreviatura de):

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

Tenga en cuenta que este enfoque tiene un inconveniente: en realidad, se crea una nueva matriz en todas y cada una de las referencias, por lo que si está utilizando una matriz muy grande, esta podría no ser la solución más eficiente. Pero si reutiliza la misma matriz (poniéndola en un atributo privado, por ejemplo), volverá a abrir la posibilidad de cambiar el contenido de la matriz.

Si desea tener una matriz (o lista) inmutable, también puede usar:

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

Pero, esto todavía tiene el riesgo de cambios, ya que aún puede volver a convertirlo en una cadena [] y alterar el contenido, como tal:

((string[]) Titles)[1] = "French";
mjepson
fuente
1
¿Cuál es el beneficio de usar la propiedad en lugar del campo en este caso?
nicolay.anykienko
2
Un campo no puede devolver un nuevo objeto en cada llamada. Una propiedad es básicamente una especie de "función disfrazada".
mjepson
1
Si se refería a la última opción, eso se puede hacer utilizando un campo o una propiedad, pero como es público, prefiero una Propiedad. Nunca uso un campo público desde la introducción de Propiedades.
mjepson
1
Hay otro inconveniente en el primer enfoque: no obtendrá un error de compilación si asigna Titles[0], por ejemplo, en efecto, el intento de asignación se ignora silenciosamente. Combinado con la ineficiencia de recrear la matriz cada vez, me pregunto si vale la pena mostrar este enfoque. Por el contrario, el segundo enfoque es eficiente, y tienes que salir de tu camino para vencer la inmutabilidad.
mklement0
6

Si declara una matriz detrás de una interfaz IReadOnlyList, obtiene una matriz constante con valores constantes que se declara en tiempo de ejecución:

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

Disponible en .NET 4.5 y superior.

Richard Garside
fuente
6

Una solución .NET Framework v4.5 + que mejora la respuesta de tdbeckett :

using System.Collections.ObjectModel;

// ...

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

Nota: Dado que la colección es conceptualmente constante, puede tener sentido staticdeclararla a nivel de clase .

Lo anterior:

  • Inicializa el campo de respaldo implícito de la propiedad una vez con la matriz.

    • Tenga en cuenta que { get; }- es decir, declarar solamente una propiedad getter - es lo que hace que la propiedad en sí implícitamente sólo lectura (tratando de combinar readonlycon la { get; }realidad, es un error de sintaxis).

    • Alternativamente, puede omitir { get; }y agregar readonlypara crear un campo en lugar de una propiedad, como en la pregunta, pero exponer a los miembros de datos públicos como propiedades en lugar de campos es un buen hábito para formar.

  • Crea una estructura de tipo matriz (que permite el acceso indexado ) que es verdadera y robustamente de solo lectura (conceptualmente constante, una vez creada), ambos con respecto a:

    • evitar la modificación de la colección en su conjunto (como eliminar o agregar elementos o asignar una nueva colección a la variable).
    • evitando la modificación de elementos individuales .
      (Incluso la modificación indirecta no es posible, a diferencia de una IReadOnlyList<T>solución, donde un (string[])reparto se puede utilizar para obtener acceso de escritura a los elementos , como se muestra en la útil respuesta de mjepsen .
      La misma vulnerabilidad se aplica a la IReadOnlyCollection<T> interfaz , que, a pesar de la similitud en el nombre a la clase ReadOnlyCollection , ni siquiera admite el acceso indexado , lo que lo hace fundamentalmente inadecuado para proporcionar acceso tipo matriz).
mklement0
fuente
1
@mortb: Desafortunadamente, IReadOnlyCollectionno admite acceso indexado, por lo que no se puede usar aquí. Además, like IReadOnlyList(que tiene acceso indexado) es susceptible a la manipulación de elementos al volver a enviarlos string[]. En otras palabras: ReadOnlyCollection(a la que no puede convertir una matriz de cadenas) es la solución más sólida. No usar un captador es una opción (y he actualizado la respuesta para tener en cuenta eso), pero con los datos públicos probablemente sea mejor quedarse con una propiedad.
mklement0
5

Para mis necesidades defino staticmatriz, en lugar de imposible consty funciona: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

ALZ
fuente
1
Simplemente eliminar constdel ejemplo OP también funciona, pero eso (o su respuesta) permite cambiar tanto: Titlesinstancia como cualquier valor. Entonces, ¿cuál es el punto en esta respuesta?
Sinatr
@Sinatr, respondí esto hace 3 años, cuando comencé a trabajar en C #. Lo dejé, ahora estoy en el mundo de Java. Tal vez olvidé agregarreadonly
ALZ
Después de pensarlo dos veces, su respuesta es una forma directa de hacer que el código OP funcione , sin ninguna const/ readonlyconsideración, simplemente haciéndolo funcionar (como si constfuera un error de sintaxis). Para algunas personas, parece ser una respuesta valiosa (¿tal vez también intentaron usarla constpor error?).
Sinatr
5

Podría adoptar un enfoque diferente: defina una cadena constante para representar su matriz y luego divida la cadena en una matriz cuando la necesite, p. Ej.

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

Este enfoque le proporciona una constante que puede almacenarse en la configuración y convertirse en una matriz cuando sea necesario.

Alastair
fuente
8
Creo que el costo de realizar la división supera con creces cualquier beneficio obtenido al definir const. ¡Pero +1 para un enfoque único y pensar fuera de la caja! ;)
Radderz
Estaba a punto de publicar la misma solución y luego vi esto, al contrario de los comentarios duros y negativos, esto era realmente perfecto para mi escenario, donde necesitaba pasar una constante a un Atributo, y luego dividí el valor en el constructor de atributos para Conseguí lo que necesitaba. y no veo ninguna razón por la que tendrá un costo de rendimiento ya que no se crean atributos para cada instancia.
Kalpesh Popat
4

En aras de la exhaustividad, ahora también tenemos ImmutableArrays a nuestra disposición. Esto debería ser realmente inmutable:

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

Requiere System.Collections.Immutable NuGet reference

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

shurik
fuente
3

Esta es una manera de hacer lo que quieres:

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 muy similar a hacer una matriz de solo lectura.

tdbeckett
fuente
8
Puedes hacer eso como public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; no es necesario volver a crear la lista en cada recuperación si la convierte en una ReadOnlyCollection de todos modos.
Nyerguds
3

Esta es la única respuesta correcta. Actualmente no puedes hacer esto.

Todas las otras respuestas sugieren usar variables estáticas de solo lectura que son similares , pero no iguales a una constante. Una constante está codificada en el ensamblaje. Una variable de solo lectura estática se puede configurar una vez, probablemente a medida que se inicializa un objeto.

Estos a veces son intercambiables, pero no siempre.

Roger Hill
fuente
2

Creo que solo puedes hacer que sea de solo lectura.

skaz
fuente
2

Las matrices son probablemente una de esas cosas que solo se pueden evaluar en tiempo de ejecución. Las constantes deben evaluarse en tiempo de compilación. Intente usar "readonly" en lugar de "const".

nemke
fuente
0

Como alternativa, para solucionar el problema de elementos que se pueden modificar con una matriz de solo lectura, puede usar una propiedad estática en su lugar. (Los elementos individuales aún se pueden cambiar, pero estos cambios solo se realizarán en la copia local de la matriz).

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

Por supuesto, esto no será particularmente eficiente ya que cada vez se crea una nueva matriz de cadenas.

Conejera
fuente
0

Mejor 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 };
Herman Schoenfeld
fuente