2011-02-28 17 views
284

¿Es posible escribir algo similar a lo siguiente?Declare a const array

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

estática podría utilizarse, cadena pública estática [] Títulos = nueva cadena [] {"Alemán", "Español"}; –

Respuesta

468

Sí, pero hay que declararlo readonly en lugar de const:

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

La razón es que const se puede aplicar sólo 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.

Al declararlo readonly resuelve 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 en última instancia se quiere lograr, también se podría considerar que se declara una enumeración:

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

Tenga en cuenta que la * matriz * aquí no es solo de lectura, por supuesto; Títulos [2] = "Galés"; funcionaría bien en el tiempo de ejecución –

+38

Probablemente también lo quiera estático – mayu

+3

¿qué tal declarar una matriz "const" en un cuerpo de método, no en la clase uno? – serhio

2

Creo que solo puede hacerlo de forma legible.

1

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

38

no se puede crear una matriz 'const' porque las matrices son objetos y sólo puede ser creado 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 "de solo lectura". Esto tiene el mismo efecto que como const, excepto que el valor puede establecerse en tiempo de ejecución. Solo puede ser establecido una vez y, a partir de entonces, es un valor de solo lectura (es decir, const).

48

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

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(); 
+18

En .NET 4.5 y superior, puede declarar una lista como IReadOnlyList en lugar de IList . –

6

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

+1

Simplemente eliminar 'const' del ejemplo OP también funciona, pero esa (o su respuesta) permite cambiar ambas: la instancia de 'Títulos' y cualquier valor. Entonces, ¿cuál es el punto en esta respuesta? – Sinatr

+0

@Sinatr, respondí esto hace 3 años, cuando empecé a trabajar en C#. Lo dejé, ahora estoy en el mundo de Java. Tal vez se me olvidó añadir 'readonly' – ALZ

+0

Después de pensarlo dos veces, su respuesta es una directa * cómo hacer que el código OP de trabajo *, sin ningún tipo de' const'/'consideraciones readonly', simplemente * * por lo que es trabajar (como si' const' fue un error de sintaxis). Para algunas personas, parece ser una * valiosa * respuesta (¿quizás también trataron de usar ['const'] (https://msdn.microsoft.com/en-us/library/e6w8fe1b.aspx) por error?). – Sinatr

7

puede tomar un enfoque diferente: definir una constante de cadena para representar la matriz y luego dividir la cadena en una matriz cuando la necesita, por ejemplo

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 configuración y convertirse en una matriz cuando sea necesario.

Alastair

+7

Esto es bastante inútil. Tampoco resuelve el problema de "los elementos en la matriz no son de solo lectura". – Nyerguds

+3

Creo que el costo de realizar la división supera con creces los beneficios que se obtienen definiendo const. ¡Pero +1 por un enfoque único y pensar fuera de la caja! ;) – Radderz

3

Ésta es una manera de hacer lo que quiera:

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 sólo lectura.

+8

Puede hacer eso como 'public static readonly ReadOnlyCollection Titles = new List {" German "," Spanish "," Corrects "," Wrongs "}. As ReadAdd only(); no es necesario volver a crear la lista en cada recuperación si la convierte en ReadOnlyCollection de todos modos. – Nyerguds

1

Como alternativa, para evitar el problema de los elementos que se pueden modificar con una matriz de solo lectura, puede usar una propiedad estática en su lugar. (Los elementos individuales todavía se pueden cambiar, pero estos cambios sólo se pueden hacer en la copia local de la matriz.)

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

Por supuesto, esto no va a ser particularmente eficientes como se crea una nueva matriz de cadenas cada vez .

6

Puesto que C# 6 se puede escribir como:

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

Ver también: C# : The New and Improved C# 6.0 (específicamente el capítulo "Funciones de Expresiones cuerpo y Propiedades")

Esto hará que una propiedad estática de sólo lectura, pero aún le permitirá alterar el contenido de la matriz devuelta, pero cuando vuelva a llamar a la propiedad, obtendrá nuevamente la matriz original sin modificaciones.

Para mayor claridad, este código es lo mismo que (o en realidad una abreviatura de):

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

Tenga en cuenta que hay un inconveniente de este enfoque: Una nueva matriz es en realidad una instancia en todos y cada referencia , por lo tanto, si usa 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), nuevamente se abrirá la posibilidad de cambiar el contenido de la matriz.

Si usted quiere tener un conjunto inmutable (o lista) también se puede utilizar:

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

embargo, esto todavía tiene un riesgo de cambios, ya que todavía puede lanzar de nuevo a una cadena [] y alterar el contenido, como tal:

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

¿Cuál es el beneficio de usar propiedad en lugar de campo en este caso? –

+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 estaba refiriendo a la última opción, puede hacerlo utilizando un campo o una propiedad, pero dado que es público, prefiero una Propiedad. Nunca utilizo un campo público desde la introducción de Propiedades. – mjepson

3

Un marco v4.5 .NET + solución que mejora en tdbeckett's answer:

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 hacerlo static para declararlo en el nivel clase.

lo anterior:

  • Inicializa campo 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 con readonly{ get; } es en realidad un error de sintaxis).

    • Como alternativa, puede simplemente omite el { get; } y añadir readonly para crear un campolugar de una propiedad, al igual que en la pregunta, pero la exposición de miembros de datos públicos como propiedades en lugar de campos es un buen hábito para formar .

  • crea una estructura array-como (permitiendo acceso indexado) que es verdadera y robusta de sólo lectura (conceptualmente, constante, una vez creado), tanto con respecto a:

    • que impide la modificación de la colección (por ejemplo, eliminando o añadiendo elementos o reemplazando la colección como un todo)
    • evitando la modificación de elementos individuales.
      (Incluso indirecta modificación no es posible - a diferencia de una solución IReadOnlyList<T>, donde un elenco (string[]) se puede utilizar para obtener acceso de escritura a los elementos, como se muestra en mjepsen's helpful answer
      La misma vulnerabilidad se aplica a la interfaz IReadOnlyCollection<T>,. que, a pesar de su similitud de nombre a claseReadOnlyCollection, ni siquiera es compatible con indexado acceso, por lo que es fundamentalmente inadecuado para proporcionar acceso tipo array).
+1

@mortb: Desafortunadamente, 'IReadOnlyCollection' no admite el acceso indexado, por lo que no se puede usar aquí. Además, al igual que 'IReadOnlyList' (que tiene acceso indexado), es susceptible a la manipulación del elemento volviendo a' string [] '. En otras palabras: 'ReadOnlyCollection' (que no se puede convertir en una matriz de cadenas) es la solución más robusta. No usar un getter es una opción (y he actualizado la respuesta para tener en cuenta eso), pero con _public_ data es mejor quedarse con una propiedad. – mklement0

+0

@mortb: P.S .: También actualicé la respuesta para mostrar por qué 'IReadOnlyCollection' no es una opción. – mklement0

1

Si se declara una matriz detrás de una interfaz IReadOnlyList se 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.