No se puede convertir la matriz de tipo de valor en el objeto params

Si C # puede lanzar un int a un objeto, ¿por qué no un int [] a un objeto []?

Ejemplo de progtwig simple:

void Main() { var a = new String[]{"0", "1"}; var b = new int[]{0, 1}; AssertMoreThan1(a); // No Exception AssertMoreThan1(b); // Exception } static void AssertMoreThan1(params object[] v){ if(v.Length == 1){ throw new Exception("Too Few Parameters"); } } 

Si C # puede lanzar un int a un objeto, ¿por qué no un int [] a un objeto []?

Su pregunta también podría establecerse como “¿cuáles son las reglas de covarianza para las conversiones de matriz en C #?”

Son un poco engañosas y están rotas de varias maneras interesantes y desafortunadas.

En primer lugar, deberíamos indicar claramente lo que queremos decir con “covarianza”. La covarianza es la propiedad de que una asignación conserva una relación . El mapeo aquí es “T va a la matriz de T”. La relación es “se puede convertir implícitamente”. Por ejemplo:

Giraffe se puede convertir implícitamente en Mammal .

Esa es una relación entre dos tipos. Ahora aplique el mapeo a ambos lados de la relación:

Giraffe[] se puede convertir a Mammal[] .

Si la verdad del primer enunciado siempre implica la verdad del segundo enunciado, es decir, si el mapeo preserva la verdad de la relación, se dice que el mapeo es “covariante”.

Como taquigrafía, en lugar de decir “el mapeo de T a la matriz de T es un mapeo covariante sobre la relación de conversión implícita”, simplemente decimos “las matrices son covariantes” y esperamos que el rest se entienda desde el contexto.

OK, ahora que tenemos la definición desactivada: las matrices con elementos de tipo de referencia son covariantes en C #. Trágicamente, esto es covarianza rota:

 class Mammal {} class Giraffe : Mammal {} class Tiger : Mammal {} ... Mammal[] mammals = new Giraffe[1]; 

Esto es perfectamente legal porque las matrices de elementos de tipo de referencia son covariantes en C #. Pero luego esto se bloquea en tiempo de ejecución:

 mammals[0] = new Tiger(); 

porque los mamíferos son realmente una variedad de jirafas .

Esto significa que cada vez que escribe en una matriz cuyos elementos son tipos de referencia no sellados , el tiempo de ejecución realiza una verificación de tipo y puede bloquearse si falla la verificación de tipo .

Este es mi candidato para “la peor característica de C #”, pero de hecho funciona .

Su pregunta es “¿por qué la covarianza de matrices no funciona cuando la matriz fuente es una matriz de tipo de valor y la matriz objective es una matriz de tipo de referencia?”

Porque esas dos cosas tienen una forma diferente en el tiempo de ejecución . Supongamos que tiene un byte[] con diez elementos. El almacenamiento real reservado para los elementos de la matriz es de diez bytes de longitud. Supongamos que está en una máquina de 64 bits y tiene un object[] con diez elementos. ¡El almacenamiento es ocho veces más grande!

Claramente, no se puede convertir a través de la conversión de referencia una referencia al almacenamiento de diez bytes para el almacenamiento de diez referencias de ocho bytes a bytes. Los setenta bytes adicionales no salen de la nada; alguien tiene que asignarlos.

Además: ¿ quién hace el boxeo ? Si tiene una matriz de diez objetos y cada objeto es un byte, cada uno de esos bytes está enmarcado . Pero los bytes en una matriz de bytes no están encuadrados. Entonces cuando haces la conversión, ¿quién hace el boxeo?

En general, en C #, las conversiones covariantes siempre conservan la representación . La representación de una “referencia a Animal” es exactamente la misma que la representación de “referencia a Jirafa”. Pero las representaciones de “int” y “referencia al objeto” son completamente diferentes.

Se espera que el lanzamiento de un tipo de matriz a otro no asigne ni copie una matriz enorme . Pero no podemos tener una identidad referencial entre una matriz de diez bytes y una matriz de ochenta bytes que contenga diez referencias, y por lo tanto todo el asunto simplemente se vuelve ilegal.

Ahora, entonces podría decir, bueno, ¿qué sucede cuando las representaciones son las mismas para los tipos de valores? De hecho, esto es ilegal en C #:

 int[] x = new uint[10]; 

porque en C # la regla es que solo las conversiones de matriz covariantes que involucren solo tipos de referencia son legales. Pero si lo obligas a hacerlo en tiempo de ejecución:

 int[] x = (int[])(object) new uint[10]; 

Entonces, el tiempo de ejecución lo permite porque un byte de cuatro bytes y uno de cuatro bytes tienen la misma representación.

Si quieres entender esto mejor, entonces probablemente deberías leer toda mi serie de artículos sobre cómo funciona la covarianza y la contravarianza en C #:

  • Toda la serie

  • Los detalles de la covarianza de matriz de elementos de referencia inseguros

  • Más acerca de la covarianza de conjuntos de elementos de valor

De hecho, no puedes convertir eso. Las matrices de tipo de referencia son covariantes; las matrices de tipo valor no lo son . Asi que; deberás usar uno de:

una matriz de valores encuadrados:

 var b = new object[] {0,1}; 

o podrías usar IList :

 static void AssertMoreThan1(IList v) { ... (check with .Count) } 

o generics:

 static void AssertMoreThan1(T[] v) { ... } 

El último sería mi preferencia.