2009-11-19 17 views
5

Una matriz con tipo implementa las interfaces System.Collections.IList y System.Collections.Generic.ICollection<T>, que tienen sus propias propiedades IsReadOnly. ¿Pero qué demonios está pasando aquí?Array.IsReadOnly inconsistente según la implementación de la interfaz

var array = new int[10]; 
Console.WriteLine(array.IsReadOnly); // prints "False" 

var list = (System.Collections.IList)array; 
Console.WriteLine(list.IsReadOnly); // prints "False" 

var collection = (System.Collections.Generic.ICollection<int>)array; 
Console.WriteLine(collection.IsReadOnly); // prints "True" 

La vista IList de la matriz se comporta como yo esperaría, volviendo la misma que la propia matriz, sin embargo la vista ICollection<T> de la matriz devuelve verdadero.

¿Hay alguna explicación racional para este comportamiento, o es un error de compilación/CLR? (Me sorprendería mucho si es lo último, como imaginarían que esto ya se hubiera encontrado antes, pero es tan contrario a la intuición que no puedo pensar cuál podría ser la explicación ...).

Estoy usando C# 3.0/.NET 3.5 SP1.

+1

Interesante, las observaciones de ambos dicen "Una colección que es de solo lectura no permite la adición, eliminación o modificación de elementos una vez creada la colección". Y creo que la lógica equivale a IsReadOnly =! CanAdd || ! CanRemove || ! CanEdit que debería devolver verdadero porque puede reemplazar el valor en un índice especificado de la lista. –

+0

a pero más, pero el documento Array.IsReadonly es explícito .. "Esta propiedad siempre es falsa para todas las matrices." "La matriz implementa la propiedad IsReadOnly porque la requiere la interfaz System.Collections .. ::. IList Si necesita una colección de solo lectura, use una clase System.Collections que implemente System.Collections .. ::. Interfaz IList. Una matriz que es de solo lectura no permite la adición, eliminación o modificación de elementos una vez creada la matriz. " –

Respuesta

4

Hubo un montón de agonía sobre esta decisión, como es evidente en los comentarios sobre este feedback article.

+0

Parece que esto da el razonamiento interno (que parece ser "sabemos que es basura, pero es demasiado tarde en el ciclo de lanzamiento para hacer algo al respecto"). Entonces es por diseño, pero no es bueno. –

+3

el enlace al artículo de comentarios parece estar roto. hay otro enlace? –

0

La razón de este comportamiento se reduce a 2 System.Array que tienen propiedades IsReadOnly

La primera es una propiedad normal sobre el tipo array. Esta propiedad satisface la propiedad IsReadOnly de la interfaz IList. Por alguna razón en 1.0 de CLR, consideraron que la propiedad debería ser verdadera.

El segundo es la implementación de propiedad explícita para el tipo ICollection<T> (realmente implementado por el CLR IIRC). En este caso, IsReadOnly devuelve verdadero porque el tipo Array no puede satisfacer los métodos de mutación de ICollection<T> como Add, Clear, etc ...

La verdadera pregunta es ¿por qué el cambio entre versiones? En realidad, no lo sé, pero creo que los autores determinaron que es más apropiado ver Array como de solo lectura cuando se ve como una colección separada. Si bien puede satisfacer parte de los métodos mutables, no puede satisfacerlos a todos. Por lo tanto, es más seguro verlo como readonly vs. mutable.

+0

La explicación de que 'ICollection .IsReadOnly' devuelve verdadero porque no puede satisfacer todos los métodos de la interfaz' ICollection 'no retiene agua porque' IList.IsReadOnly' devuelve falso y no puede satisfacer todos los métodos de la interfaz 'IList' tampoco. Por lo tanto, todavía no sé si esto es un error o, en caso negativo, cuál fue el fundamento de la decisión. –

+0

@Greg, creo que esto representa un cambio en el pensamiento entre v1 y v2 del CLR. Probablemente debido a quejas de los clientes, aunque no puedo encontrar ninguna evidencia de eso. – JaredPar

0

A partir de los documentos para la Array Class:

En la versión 2.0 de .NET Framework, la clase Array implementa los System.Collections.Generic.IList<T>, System.Collections.Generic.ICollection<T> y System.Collections.Generic.IEnumerable<T> interfaces genéricas. Las implementaciones se proporcionan a las matrices en tiempo de ejecución y, por lo tanto, no son visibles para las herramientas de compilación de documentación . Como resultado, los genéricos interfaces no aparecen en la sintaxis declaración de la clase Array , y hay ninguna referencia temas para los miembros de interfaz que son accesible sólo por colada una matriz para el tipo de interfaz genérica (explícitos implementaciones de interfaz). La clave cosa a tener en cuenta cuando lanzas una matriz a una de estas interfaces es que los miembros que se suman, insertar o eliminar elementos tiran NotSupportedException.

Por lo tanto, como las colecciones genéricas no admiten agregar, insertar o eliminar, IsReadOnly devuelve verdadero. ¿En cuanto a por qué no devuelve falso para System.Collections.IList?Mi conjetura sería que

var array = new int[10]; 
Console.WriteLine(array.IsReadOnly); // prints "True" 
array[0] = 5; // WTF? This is readonly. 

no era lo que querían ver. Cuando agregaron las interfaces de genéricos en v2, solo se pueden invocar cuando se ha lanzado la matriz, por lo que volver a ser verdadero para las que tienen más sentido.

+0

Eso en realidad no responde la pregunta en absoluto; no hay justificación para por qué la propiedad IsReadOnly devuelve un valor diferente para la matriz subyacente. –

+0

clarificó mi respuesta – Logan5

6

From MSDN:

IList es un descendiente de la interfaz ICollection y es el interfaz base de todas las listas no genéricos. Las implementaciones de IList se dividen en tres categorías de : de solo lectura, tamaño fijo y de tamaño variable. Un IList de solo lectura no se puede modificar. Un IList de tamaño fijo no permite la adición o eliminación de elementos , pero permite la modificación de los elementos existentes. Un IList de tamaño variable permite la adición, eliminación y modificación de elementos .

ICollection < T> interfaz no tiene un indexador, por lo que una de tamaño fijo ICollection < T> es de sólo lectura de forma automática - no hay manera de modificar un elemento existente.

Posiblemente ICollection < T> .IsFixedSize habría sido un mejor nombre de la propiedad de ICollection < T> .IsReadOnly, pero ambos implican la misma cosa - imposible añadir o eliminar elementos, es decir, el mismo que IList.IsFixedSize.

Una matriz es una lista de tamaño fijo, pero no es de solo lectura ya que los elementos se pueden modificar.

Como un ICollection < T>, es de solo lectura, ya que un ICollection < T> no tiene forma de modificar los elementos.

Esto puede parecer confuso, pero es consistente y lógico.

Qué es ligeramente inconsistente es que el genérica IList < T> interfaz tiene una propiedad heredada de IsReadOnly ICollection < T> cuya semántica son, por tanto, diferente de la IList.IsReadOnly no genérico. Imagino que los diseñadores eran conscientes de esta incoherencia, pero no pudieron volver atrás y cambiar la semántica del IList no genérico por razones de compatibilidad con versiones anteriores.

En resumen, un IList puede ser:

  • Variable de tamaño.

    IList.IsFixedSize = false

    IList.IsReadOnly = false

    ICollection < T> .IsReadOnly = false

  • de tamaño fijo (pero los elementos pueden ser modificados, por ejemplo, un Array)

    IList.IsFixedSize = true

    IList.IsReadOnly = false

    ICollection < T> .IsReadOnly = true

  • de sólo lectura (elementos no pueden ser modificados)

    IList.IsFixedSize = true

    IList.IsReadOnly = true

    ICollection < T> .IsReadOnly = true

Cuestiones relacionadas