Compartir a través de


17 Matrices

17.1 General

Una matriz es una estructura de datos que contiene un número de variables a las que se accede mediante índices calculados. Las variables contenidas en una matriz, denominadas también elementos de la matriz, son todas del mismo tipo y este tipo se conoce como tipo de elemento de la matriz.

Una matriz tiene una clasificación que determina el número de índices asociados a cada elemento de matriz. El rango de una matriz también se conoce como dimensiones de la matriz. Una matriz con un rango de uno se denomina matriz unidimensional. Una matriz con una clasificación mayor que una se denomina matriz multidimensional. Las matrices multidimensionales de tamaño específico se conocen a menudo como matrices bidimensionales, matrices tridimensionales, etc. Cada dimensión de una matriz tiene una longitud asociada que es un número entero mayor o igual que cero. Las longitudes de dimensión no forman parte del tipo de la matriz, sino que se establecen cuando se crea una instancia del tipo de matriz en tiempo de ejecución. La longitud de una dimensión determina el intervalo válido de índices para esa dimensión: para una dimensión de longitud N, los índices pueden oscilar entre 0 ambos N – 1 . El número total de elementos de una matriz es el producto de las longitudes de cada dimensión de la matriz. Si una o varias de las dimensiones de una matriz tienen una longitud de cero, se dice que la matriz está vacía.

El tipo de elemento de una matriz puede ser un tipo de matriz (§17.2.1). Estas matrices de matrices son distintas de matrices multidimensionales y se pueden usar para representar "matrices escalonadas".

Ejemplo:

int[][] pascals = 
{
    new int[] {1},
    new int[] {1, 1},
    new int[] {1, 2, 1},
    new int[] {1, 3, 3, 1}
};

ejemplo final

Cada tipo de matriz es un tipo de referencia (§8.2). El tipo de elemento de una matriz puede ser cualquier tipo, incluidos los tipos de valor y los tipos de matriz.

17.2 Tipos de matriz

17.2.1 General

Las producciones gramaticales para los tipos de matriz se proporcionan en §8.2.1.

Un tipo de matriz se escribe como un non_array_type seguido de uno o varios rank_specifiers.

Un non_array_type es cualquier tipo que no sea un array_type.

El rango de un tipo de matriz lo proporciona el rank_specifier situado más a la izquierda en el array_type: un rank_specifier indica que la matriz es una matriz con un rango de uno más el número de tokens "," en el rank_specifier.

El tipo de elemento de un tipo de matriz es el tipo que resulta de eliminar el rank_specifier más a la izquierda:

  • Un tipo de matriz del formulario T[R] es una matriz con rango R y un tipo Tde elemento que no es de matriz .
  • Un tipo de matriz del formulario T[R][R₁]...[Rₓ] es una matriz con rango R y un tipo T[R₁]...[Rₓ]de elemento .

En efecto, los rank_specifierse leen de izquierda a derecha antes del tipo final de elemento no matriz.

Ejemplo: el tipo de es T[][,,][,] una matriz unidimensional de matrices tridimensionales de matrices bidimensionales de int. ejemplo final

En tiempo de ejecución, un valor de un tipo de matriz puede ser null o una referencia a una instancia de ese tipo de matriz.

Nota: Siguiendo las reglas de §17.6, el valor también puede ser una referencia a un tipo de matriz covariante. nota final

17.2.2 El tipo System.Array

El tipo System.Array es el tipo base abstracto de todos los tipos de matriz. Existe una conversión de referencia implícita (§10.2.8) desde cualquier tipo de matriz a System.Array y a cualquier tipo de interfaz implementado por System.Array. Existe una conversión de referencia explícita (§10.3.5) desde System.Array y cualquier tipo de interfaz implementado por System.Array en cualquier tipo de matriz. System.Array no es un array_type. En su lugar, es un class_type del que se derivan todos los array_type.

En tiempo de ejecución, un valor de tipo System.Array puede ser null o una referencia a una instancia de cualquier tipo de matriz.

17.2.3 Matrices y interfaces de colección genéricas

Una matriz T[] unidimensional implementa la interfaz System.Collections.Generic.IList<T> (IList<T> para abreviar) y sus interfaces base. En consecuencia, hay una conversión implícita de T[] a IList<T> y sus interfaces base. Además, si hay una conversión de referencia implícita desde S para, T a continuación S[] , implementa IList<T> y hay una conversión de referencia implícita de S[] a IList<T> y sus interfaces base (§10.2.8). Si hay una conversión de referencia explícita de S a T , hay una conversión de referencia explícita desde S[] a IList<T> y sus interfaces base (§10.3.5).

De forma similar, una matriz T[] unidimensional también implementa la interfaz System.Collections.Generic.IReadOnlyList<T> (IReadOnlyList<T> para abreviar) y sus interfaces base. En consecuencia, hay una conversión implícita de T[] a IReadOnlyList<T> y sus interfaces base. Además, si hay una conversión de referencia implícita desde S para, T a continuación S[] , implementa IReadOnlyList<T> y hay una conversión de referencia implícita de S[] a IReadOnlyList<T> y sus interfaces base (§10.2.8). Si hay una conversión de referencia explícita de S a T , hay una conversión de referencia explícita desde S[] a IReadOnlyList<T> y sus interfaces base (§10.3.5).

Ejemplo: por ejemplo:

class Test
{
    static void Main()
    {
        string[] sa = new string[5];
        object[] oa1 = new object[5];
        object[] oa2 = sa;

        IList<string> lst1 = sa;  // Ok
        IList<string> lst2 = oa1; // Error, cast needed
        IList<object> lst3 = sa;  // Ok
        IList<object> lst4 = oa1; // Ok

        IList<string> lst5 = (IList<string>)oa1; // Exception
        IList<string> lst6 = (IList<string>)oa2; // Ok

        IReadOnlyList<string> lst7 = sa;        // Ok
        IReadOnlyList<string> lst8 = oa1;       // Error, cast needed
        IReadOnlyList<object> lst9 = sa;        // Ok
        IReadOnlyList<object> lst10 = oa1;      // Ok
        IReadOnlyList<string> lst11 = (IReadOnlyList<string>)oa1; // Exception
        IReadOnlyList<string> lst12 = (IReadOnlyList<string>)oa2; // Ok
    }
}

La asignación lst2 = oa1 genera un error en tiempo de compilación, ya que la conversión de object[] a IList<string> es una conversión explícita, no implícita. La conversión (IList<string>)oa1 hará que se produzca una excepción en tiempo de ejecución, ya que oa1 hace referencia a y object[] no a string[]. Sin embargo, la conversión (IList<string>)oa2 no hará que se produzca una excepción, ya que oa2 hace referencia a .string[]

ejemplo final

Siempre que haya una conversión de referencia implícita o explícita de S[] a IList<T>, también hay una conversión de referencia explícita de IList<T> y sus interfaces base a S[] (§10.3.5).

Cuando un tipo S[] de matriz implementa IList<T>, algunos de los miembros de la interfaz implementada pueden producir excepciones. El comportamiento preciso de la implementación de la interfaz está fuera del ámbito de esta especificación.

Creación de matrices 17.3

Las instancias de matriz se crean mediante array_creation_expression s (§12.8.16.5) o por declaraciones de variables locales o de campo que incluyen un array_initializer (§17.7). Las instancias de matriz también se pueden crear implícitamente como parte de la evaluación de una lista de argumentos que implica una matriz de parámetros (§15.6.2.4).

Cuando se crea una instancia de matriz, se establecen la clasificación y la longitud de cada dimensión y, a continuación, permanecen constantes durante toda la duración de la instancia. En otras palabras, no es posible cambiar el rango de una instancia de matriz existente ni es posible cambiar el tamaño de sus dimensiones.

Una instancia de matriz siempre es de un tipo de matriz. El System.Array tipo es un tipo abstracto que no se puede crear una instancia.

Los elementos de las matrices creadas por array_creation_expressions siempre se inicializan con su valor predeterminado (§9.3).

Acceso al elemento Array 17.4

Se accede a los elementos de matriz mediante expresiones de element_access (§12.8.11.2) del formulario A[I₁, I₂, ..., Iₓ], donde A es una expresión de un tipo de matriz y cada Iₑ una de ellas es una expresión de tipo int, uint, long, ulongo se puede convertir implícitamente a uno o varios de estos tipos. El resultado de un acceso a elementos de matriz es una variable, es decir, el elemento de matriz seleccionado por los índices.

Los elementos de una matriz se pueden enumerar mediante una foreach instrucción (§13.9.5).

17.5 Miembros de matriz

Cada tipo de matriz hereda los miembros declarados por el System.Array tipo .

17.6 Matriz covarianza

Para dos reference_type s y , si existe una conversión de referencia implícita (§10.2.8) o conversión de referencia explícita (§10.3.5) existe de A a B, la misma conversión de referencia también existe desde el tipo de matriz al tipo A[R] B[R]de matriz , donde R es cualquier rank_specifier dado (pero el mismo para ambos tipos de matriz).BA Esta relación se conoce como covarianza de matriz. La covarianza de matriz, en particular, significa que un valor de un tipo A[R] de matriz podría ser realmente una referencia a una instancia de un tipo B[R]de matriz , siempre que exista una conversión de referencia implícita de B a A.

Debido a la covarianza de matriz, las asignaciones a elementos de matrices de tipo de referencia incluyen una comprobación en tiempo de ejecución que garantiza que el valor que se asigna al elemento de matriz es realmente de un tipo permitido (§12.21.2).

Ejemplo:

class Test
{
    static void Fill(object[] array, int index, int count, object value) 
    {
        for (int i = index; i < index + count; i++)
        {
            array[i] = value;
        }
    }

    static void Main() 
    {
        string[] strings = new string[100];
        Fill(strings, 0, 100, "Undefined");
        Fill(strings, 0, 10, null);
        Fill(strings, 90, 10, 0);
    }
}

La asignación a en el Fill método incluye implícitamente una comprobación en tiempo de ejecución, que garantiza que value sea una null referencia o una referencia a un objeto de un tipo compatible con el tipo de elemento real de array.array[i] En Main, las dos primeras invocaciones de Fill se realizan correctamente, pero la tercera invocación hace que se produzca una System.ArrayTypeMismatchException excepción al ejecutar la primera asignación en array[i]. La excepción se produce porque un cuadro int no se puede almacenar en una string matriz.

ejemplo final

La covarianza de matriz no se extiende específicamente a matrices de value_types. Por ejemplo, no existe ninguna conversión que permita que un int[] objeto se trate como .object[]

Inicializadores de matriz 17.7

Los inicializadores de matriz se pueden especificar en declaraciones de campo (§15.5), declaraciones de variables locales (§13.6.2) y expresiones de creación de matrices (§12.8.16.5):

array_initializer
    : '{' variable_initializer_list? '}'
    | '{' variable_initializer_list ',' '}'
    ;

variable_initializer_list
    : variable_initializer (',' variable_initializer)*
    ;
    
variable_initializer
    : expression
    | array_initializer
    ;

Un inicializador de matriz consta de una secuencia de inicializadores de variables, incluidos los tokens "" y "{}" y separados por tokens ",". Cada inicializador de variable es una expresión o, en el caso de una matriz multidimensional, un inicializador de matriz anidada.

El contexto en el que se usa un inicializador de matriz determina el tipo de la matriz que se va a inicializar. En una expresión de creación de matriz, el tipo de matriz precede inmediatamente al inicializador o se deduce de las expresiones del inicializador de matriz. En una declaración de campo o variable, el tipo de matriz es el tipo del campo o variable que se declara. Cuando se usa un inicializador de matriz en una declaración de campo o variable,

int[] a = {0, 2, 4, 6, 8};

simplemente es breve para una expresión de creación de matriz equivalente:

int[] a = new int[] {0, 2, 4, 6, 8};

Para una matriz unidimensional, el inicializador de matriz constará de una secuencia de expresiones, cada una con una conversión implícita al tipo de elemento de la matriz (§10.2). Las expresiones inicializan los elementos de matriz en orden creciente, empezando por el elemento en el índice cero. El número de expresiones del inicializador de matriz determina la longitud de la instancia de matriz que se va a crear.

Ejemplo: el inicializador de matriz anterior crea una int[] instancia de longitud 5 y, a continuación, inicializa la instancia con los valores siguientes:

a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;

ejemplo final

Para una matriz multidimensional, el inicializador de matriz tendrá tantos niveles de anidamiento como hay dimensiones en la matriz. El nivel de anidamiento más externo corresponde a la dimensión situada más a la izquierda y el nivel de anidamiento más interno corresponde a la dimensión situada más a la derecha. La longitud de cada dimensión de la matriz viene determinada por el número de elementos en el nivel de anidamiento correspondiente en el inicializador de matriz. Para cada inicializador de matriz anidada, el número de elementos será el mismo que los demás inicializadores de matriz en el mismo nivel.

Ejemplo: Ejemplo:

int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};

crea una matriz bidimensional con una longitud de cinco para la dimensión situada más a la izquierda y una longitud de dos para la dimensión situada más a la derecha:

int[,] b = new int[5, 2];

e inicializa la instancia de matriz con los valores siguientes:

b[0, 0] = 0; b[0, 1] = 1;
b[1, 0] = 2; b[1, 1] = 3;
b[2, 0] = 4; b[2, 1] = 5;
b[3, 0] = 6; b[3, 1] = 7;
b[4, 0] = 8; b[4, 1] = 9;

ejemplo final

Si se proporciona una dimensión distinta del extremo derecho con la longitud cero, se supone que las dimensiones posteriores también tienen longitud cero.

Ejemplo:

int[,] c = {};

crea una matriz bidimensional con una longitud de cero para la dimensión situada más a la izquierda y a la derecha:

int[,] c = new int[0, 0];

ejemplo final

Cuando una expresión de creación de matriz incluye longitudes de dimensión explícitas y un inicializador de matriz, las longitudes serán expresiones constantes y el número de elementos en cada nivel de anidamiento coincidirá con la longitud de dimensión correspondiente.

Ejemplo: Estos son algunos ejemplos:

int i = 3;
int[] x = new int[3] {0, 1, 2}; // OK
int[] y = new int[i] {0, 1, 2}; // Error, i not a constant
int[] z = new int[3] {0, 1, 2, 3}; // Error, length/initializer mismatch

Aquí, el inicializador da y como resultado un error en tiempo de compilación porque la expresión de longitud de dimensión no es una constante y el inicializador da como z resultado un error en tiempo de compilación porque la longitud y el número de elementos del inicializador no están de acuerdo.

ejemplo final

Nota: C# permite una coma final al final de un array_initializer. Esta sintaxis proporciona flexibilidad para agregar o eliminar miembros de dicha lista y simplifica la generación de máquinas de dichas listas. nota final