2009-02-27 11 views
34

¿Alguien puede aclarar la palabra clave C# is por favor? En particular estas 2 preguntas:¿Por qué "int [] es uint [] == verdadero" en C#

Q1) línea 5; ¿Por qué esto es verdad?

Q2) línea 7; ¿Por qué no hay excepción de lanzamiento?

public void Test() 
{ 
    object intArray = new int[] { -100, -200 };    

    if (intArray is uint[]) //why does this return true? 
    { 
     uint[] uintArray = (uint[])intArray; //why no class cast exception? 

     for (int x = 0; x < uintArray.Length; x++) 
     { 
      Console.Out.WriteLine(uintArray[x]); 
     } 
    } 
} 

La descripción de MSDN no aclara la situación. Establece que is devolverá verdadero si se cumple alguna de estas condiciones. (Http://msdn.microsoft.com/en-us/library/scekt9xw(VS.71).aspx>MDSN artículo)

 
expression is not null. 
expression can be cast to type. 

No creo que se puede hacer un reparto válida de int [] en uint []. Porque:

A) Este código no se compila:

int[] signed = new int[] { -100 }; 
uint[] unsigned = (uint[])signed; 

B) Hacer el molde en el depurador da un error:

(uint[])signed 
"Cannot convert type 'int[]' to 'uint[]'" 

Efectivamente, si la línea 3 fue int [] en lugar de objeto, entonces nunca compilaría. Lo que me lleva a una pregunta final relacionada con la Q2.

Q3) ¿Por qué C# genera un error de conversión/conversión en el depurador y el compilador pero no en el tiempo de ejecución?

+2

¿Dónde está Eric Lippert cuando lo necesitas? –

+4

Mal, esto es Stack Overflow, quieres decir "¿Dónde está Jon Skeet cuando lo necesitas?" ;) – Ash

+0

Compruebe mi respuesta, actualicé con un enlace a un video que creo que explica el problema subyacente. ¡Echale un vistazo! –

Respuesta

34

C# y el CLR tienen algo diferentes reglas de conversión.

no se puede directamente fundido entre int[] y uint[] en C# porque el lenguajeno se cree ningún tipo de conversión está disponible. Sin embargo, si va por object, el resultado depende de la CLI. Desde la sección de especificaciones CLI 8.7 (espero - estoy citando un email exchange I had on this topic with Eric Lippert hace un tiempo):

Signed and unsigned integral primitive types can be assigned to each other; e.g., int8 := uint8 is valid. For this purpose, bool shall be considered compatible with uint8 and vice versa, which makes bool := uint8 valid, and vice versa. This is also true for arrays of signed and unsigned integral primitive types of the same size; e.g., int32[] := uint32[] is valid.

(No he comprobado, pero supongo que este tipo de conversión de tipo de referencia es válido es lo que hace is también es cierto.)

Es un poco desafortunado que haya desconexiones entre el lenguaje y el motor de ejecución subyacente, pero es bastante inevitable a la larga, sospecho. Hay algunos otros casos como este, pero la buena noticia es que rara vez parecen causar un daño significativo.

EDIT: Como Marc borra su respuesta, he vinculado con el correo completo de Eric, tal como fue anunciado en el directorio C# grupo de noticias.

+0

suena como otro candidato para la pregunta "¿qué puedes hacer en MSIL no puedes en C#"? convección directa sin tener que intercambiar mensajes con un objeto/Variable de matriz para aplacar el compilador de C# – ShuggyCoUk

+0

@ShuggyCoUk: Buena llamada: actualizaré mi respuesta, enlazando esta pregunta. –

+1

mind = blown ... – Mehrdad

1

Sugerencia:

Declarar intArray que "int [] intArray" en lugar de "objeto intArray" permitirá que el compilador para recoger el inválido C# fundido. A menos que sea absolutamente necesario usar un objeto, tomaría ese enfoque.

Re Q2, Q3:

En tiempo de ejecución que han intentado envolver el molde en un bloque checked?

De este artículo en MSDN:

By default, an expression that contains only constant values causes a compiler error if the expression produces a value that is outside the range of the destination type. If the expression contains one or more non-constant values, the compiler does not detect the overflow.

...

By default, these non-constant expressions are not checked for overflow at run time either, and they do not raise overflow exceptions. The previous example displays -2,147,483,639 as the sum of two positive integers.

Overflow checking can be enabled by compiler options, environment configuration, or use of the checked keyword.

Como se dice, puede hacer cumplir la comprobación de desbordamiento de manera más global a través de una configuración del compilador o el entorno de configuración.

En su caso, esto es probablemente deseable, ya que causará un error de tiempo de ejecución que garantizará que el número no válido válido para el desbordamiento de números con signo no se produzca en silencio.

[Actualización] Después de probar este código, encontré que el uso de una declaración de tipo de objeto en lugar de int [] parece omitir el sintaxis de conversión C# estándar, independientemente de si está activada o no.

Como dijo JS, cuando usa object, está sujeto a las reglas CLI y aparentemente permiten que esto ocurra.

Re Q1:

Esto está relacionado con lo anterior. En resumen, dado que el elenco involucrado no arroja una excepción (según la configuración de desbordamiento actual). Si esta es una buena idea es otra pregunta.

From MSDN:

An "is" expression evaluates to true if the provided expression is non-null, and the provided object can be cast to the provided type without causing an exception to be thrown.

+0

Pero no responde su primera pregunta, "¿por qué (intArray is uint []) " –

+0

No, comprobado no capta esto, sorprendentemente. El depurador está completamente confundido por los contenidos (que muestra solo? Para los elementos en el inspector), y obtener el valor obtiene el valor ajustado. –

+0

Mismo comportamiento en mi prueba. ¿Dónde está Jon Skeet cuando lo necesitas? – Ash

0

supongo hacia atrás compatablility con .NET 1: Todavía estoy un poco difusa sobre los detalles, pero que creo el tipo CLR de todas las matrices es simplemente System.array, con propiedades Tipo adicionales para buscar el tipo de elemento. 'es' probablemente no explicaba eso en CLR v1, y ahora debe mantener eso.

No funciona en el caso (uint[])(new int[]{}) es probablemente debido al compilador de C# (no el tiempo de ejecución de CLR) que es capaz de hacer una comprobación de tipo más estricta.

También, las matrices son simplemente escriba inseguro en general:

Animal[] tigers = new Tiger[10]; 
tigers[3] = new Elephant(); // ArrayTypeMismatchException 
0

OK,

Intento tomar una puñalada a esto.

En primer lugar, la documentación dice: "Comprueba si un objeto es compatible con un tipo determinado". También dice que SI el tipo de la izquierda es "moldeable" (puede convertir sin excepción) al tipo el derecho, y la expresión evalúa como no nulo, la palabra clave "es" se evaluará a true.

mirada a Jon Skeet para la otra respuesta. Él lo dijo más elocuentemente que yo. Tiene razón, si hay una conversión disponible, aceptará su sintaxis, luego podría escribir la suya propia, pero parecería excesivo en esta situación.

Referencia: http://msdn.microsoft.com/en-us/library/scekt9xw(VS.80).aspx

4

Ahora que es interesante. Encontré esto en el estándar ECMA-335. 4.3 clase de cast. Tenga en cuenta que:

  • Las matrices se heredan de System.Array.

  • Si Foo se puede convertir en bar, a continuación, Foo [] se puede convertir en bar [].

  • A los efectos de la nota 2 anterior, las enumeraciones se tratan como su tipo subyacente: así E1 [] se puede convertir a E2 [] si E1 y E2 comparten un tipo subyacente.

Puedes invocar int to uint, pero que se comporta así es muy extraño. Visual Studio no reconoce nada de esto, incluso el reloj, cuando el depurador está conectado solo muestra un signo de interrogación '?'.

Es posible que desee echar un vistazo a this, adelantar unos 10 minutos y escuchar a Anders explicar la implementación de la matriz covariante. Creo que ese es el problema subyacente fundamental aquí.

Cuestiones relacionadas