2010-11-30 13 views
13

En matrices de tipo de referencia .NET son covariantes. Esto se considera un error. Sin embargo, no veo por qué esto es tan malo en cuenta lo siguiente código:¿Por qué la co-varianza de array se considera tan horrible?

string[] strings = new []{"Hey there"}; 
object[] objects = strings; 
objects[0] = new object(); 

Oh no, esto se compila y se producirá un error en tiempo de ejecución. Como tratamos de meter un objeto en una cadena []. Está bien, acepto que huele mal, pero una T [] se extiende de la matriz y también implementa IList (y IList<T>, me pregunto si se implementa IList<BaseType> ...>. Tanto la matriz y IList nos permiten hacer el mismo error horrible.

string[] strings = new []{"Hey there"}; 
Array objects = strings; 
objects.SetValue(new object(),new[]{0}); 

versión IList

string[] strings = new []{"Hey there"}; 
IList objects = strings; 
objects[0] = new object(); 

el T clases son generadas por el CLR, y tienen que incluir una verificación de tipos en el equivalente del método set_Item [] (matrices no tienen realmente uno).

es el ¿Le preocupa que el ajuste a T [] tenga que hacer la comprobación de tipo en el tiempo de ejecución (que viola la seguridad de tipo que espera al momento de la compilación)? ¿Por qué se considera dañino que las matrices exhiban esta propiedad cuando existen medios equivalentes para dispararse en el pie a través de los medios indicados anteriormente?

+0

Quizás quiso decir objects [0] = new object(); ? –

+0

sí, buena captura! –

Respuesta

13

Sí, IList y Array te permiten cometer el mismo error, ya que para empezar son API de tipo débil.

Arrays miran me gusta que están fuertemente tipados (en tiempo de compilación) pero en realidad no lo son. Ellos podrían tan fácilmente han sido seguros (y más rápidos) pero no lo son. Es solo una oportunidad desaprovechada tanto para el rendimiento como para la seguridad en tiempo de compilación :(

+0

¿La fluctuación de fase no puede optimizar las comprobaciones de tipo? –

+0

@Michael B: puede ser capaz de * en * algunas situaciones, pero no en todas. (Por ejemplo, si es un tipo sellado, entonces creo que debería estar bien omitir el cheque.) Es más la oportunidad perdida de la seguridad del tipo de tiempo de compilación lo que me molesta, para ser honesto. Las API que parecen seguras para el tipo pero no lo son son molestas :( –

+0

Estoy de acuerdo en que mi mayor problema no es el rendimiento, ya que las matrices tienden a ser muy rápidas, y la verificación del tiempo de compilación es el problema mayor. 'string []' implementa ambos 'IList ' y 'IList ' lo que significa que obtenemos esa horrible forma de covarianza transferida a lo largo de la línea. –

0

Un costo de varianza de la matriz es que las asignaciones a una matriz de un tipo de referencia no sellado son un poco más caras, pero considerando las asignaciones a los tipos de referencia ya hacer un poco relacionado con la GC supongo que el coste no es significativo.

22

En las matrices de tipo de referencia .NET son co-variante. Esto se considera un error.

seguridad de tipos rompiendo la covarianza de la matriz es considerado por algunas personas ser un error en el diseño de .NET. No es tan considerado por todas las personas. No considero que sea un error ; Considero que es una elección desafortunada. Todos los procesos de diseño implican elecciones entre alternativas indeseables. En este caso, la elección fue entre agregar una conversión implícita insegura que impone un costo de tiempo de ejecución en todas las escrituras de matriz, o construir un sistema de tipo que no podría implementar fácilmente el sistema de tipo Java. Esa es una elección difícil y los diseñadores del sistema de tipos tomaron la mejor decisión posible con la información que tenían.

Esa explicación, por supuesto, es mera preguntando mendicidad; ¿No es entonces simplemente el caso de que los diseñadores de Java cometieron un error? Posiblemente sí, posiblemente no; es probable que los diseñadores de Java también enfrenten concesiones en el diseño de su sistema de tipos. A todos los expertos en la historia del desarrollo del sistema de tipos de Java que les gustaría comentar aquí cuáles fueron esos intercambios, me interesaría saber.

Yo, con la ventaja de diez años de retrospectiva, personalmente hubiera preferido que los diseñadores del sistema de tipo .NET hubieran elegido evitar la covarianza de matriz de seguridad.Pero eso no hace que esa elección sea un "error", simplemente lo hace un poco desafortunado.

¿Le preocupa que la configuración de un T [] tenga que ver la comprobación de tipo en el tiempo de ejecución (lo que infringe la seguridad de tipo que espera en tiempo de compilación)?

Sí. Significa que el código que parece que debería ejecutarse correctamente siempre puede fallar en el tiempo de ejecución. Y significa que el código correcto tiene una penalización de rendimiento impuesta sobre él.

¿Por qué se considera perjudicial que los arreglos exhiban esta propiedad cuando hay medios equivalentes para dispararse en el pie a través de los medios indicados anteriormente?

Esta es una pregunta extraña. La pregunta es esencialmente "Ya tengo dos pistolas con las que puedo dispararme en el pie, así que ¿por qué se considera dañino dispararme en el pie con un tercero?"

La existencia de dos patrones peligrosos que infringen la seguridad de tipo no hace que un tercer patrón sea menos peligroso.

Las características de lenguaje y tiempo de ejecución que violan la seguridad del tipo están ahí para aquellos momentos en que usted sabe absolutamente que lo que hace es seguro, incluso si el compilador no lo sabe. Si no comprende esas características lo suficientemente bien como para usarlas de manera segura, no las use.

+0

Me gustaría que fueran con tu idea, porque parece que habías previsto muchos de estos tipos de problemas antes de que se tomen las decisiones. Tendría más sentido. Pero supongo que generalmente van con la opinión de la mayoría, supongo, ¿verdad? –

+3

@Joan: de ninguna manera preví nada de esto. Estoy hablando con el beneficio de diez años de retrospectiva. –

+1

Como siempre, su visión es muy útil. Intento crear una API que pueda usarse de forma tardía, así que estoy tratando de decidir si debo exponer algo como 'objeto []' que en realidad podría ser una 'cadena []' o exponerlo como un 'IList'. Quiero que el contenido de las matrices sea mutable por un usuario final. Supongo que la mejor pregunta es si debo usar la covarianza de matriz o no, ya que podría dar una impresión incorrecta a un usuario final. –

1

Creo que su nota sobre IList señala algo que vale la pena considerar aquí.

Es bastante útil que IList se implemente mediante matrices. También es útil que haya otras colecciones que lo implementen.

Ahora, en estos días, de hecho durante los últimos 5 años, a menudo hemos encontrado más útil (o igualmente útil y más seguro) para tratar con IList<T>.

Sin embargo, antes de .NET2.0, no teníamos IList<T>, solo teníamos IList. Bastantes casos en los que uno podría moverse entre arreglos y otras colecciones donde fiddlier (en el mejor de los casos) antes de los genéricos que en muchos casos ahora nos permiten movernos entre colecciones tipadas y matrices mecanografiadas con mayor confianza.

Como tal, los argumentos a favor de las matrices covariantes fueron mayores cuando se tomaron las decisiones pertinentes, que ahora. Y que se basan en decisiones similares en Java cuando no tenía genéricos solo aumenta este hecho.

Cuestiones relacionadas