2009-09-11 21 views
11

Tener este código ...¿Hay algo mágico en ReadOnlyCollection

var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 }); 
b[2] = 3; 

me sale un error de compilación en la segunda línea. Esperaría un error de tiempo de ejecución ya que ReadOnlyCollection<T> implementa IList<T> y this[T] tienen un setter en la interfaz IList<T>.

He intentado replicar la funcionalidad de ReadOnlyCollection, pero eliminar el setter de this[T] es un error de compilación.

Respuesta

16

El indexer se implementa con explícita implementación de la interfaz, por lo que sólo será capaz de acceder a él si lo hace:

IList<int> b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 }); 
b[2] = 3; 

o

var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 }); 
((IList<int>)b)[2] = 3; 

Por supuesto, va a fracasar después en tiempo de ejecución ...

Esto es totalmente deliberada y útil - que significa que cuando el compilador sabe es un ReadOnlyCollection, los bits no soportadas de funcionalidad no están disponibles para usted, ayudando a desviarlo del fracaso del tiempo de ejecución.

Es un paso interesante y relativamente inusual, sin embargo, efectivamente implementando la mitad de una propiedad/indexador implícitamente, y la mitad explícitamente.

Contrariamente a mis pensamientos anteriores, creo que en realidad ReadOnlyCollection<T> implementa todo el indexador de manera explícita, pero también proporciona un indexador pública de sólo lectura.En otras palabras, es algo como esto:

T IList<T>.this[int index] 
{ 
    // Delegate interface implementation to "normal" implementation 
    get { return this[index]; } 
    set { throw new NotSupportedException("Collection is read-only."); } 
} 

public T this[int index] 
{ 
    get { return ...; } 
} 
+0

Bien, pero ¿cómo puedo replicar la funcionalidad de ReadOnlyCollection usando la implementación explícita? No veo cómo puede eliminar un método o propiedad de la interfaz. –

+0

@EsbenP: no se puede eliminar un método de la interfaz ... pero puede hacerlo solo disponible cuando el tipo estático de la referencia es la interfaz en lugar de la clase que implementa la interfaz. –

+0

Ok, si tengo dos controladores paso a paso, una de ellas la implementación de IList explícitamente funciona T IList servicio [int index] { obtener { fuente de retorno [índice]; } conjunto { throw new NotImplementedException(); }} T pública esta [int index] { obtener { fuente de retorno [índice]; } } –

2

Implementa IList.Items de forma explícita, lo que hace que no sea público, y tendrá que lanzar a la interfaz para llegar a su implementación, e implementa un nuevo [...] indexador, que se usa en su lugar, que solo tiene un get-accessor.

Si lanza la colección a IList, su código se compilará pero fallará en tiempo de ejecución.

Por desgracia, no sé cómo hacer esto en C#, ya que escribir un indizador en C# implica el uso de la palabra clave this, y no puedo escribir esto:

T IList<T>.this[int index] { get; set; } 
+0

@Lasse: Se puede escribir que - con las implementaciones adecuadas. El problema es que no se puede implementar la mitad explícitamente y la mitad implícitamente, por lo que puedo decir. –

+0

No es necesario, si puede escribir eso, escriba al colocador simplemente lanzando una excepción, y luego implementará un público este [..] indexador con solo el getter. –

1

No hay magia, la ReadOnlyCollection simplemente tienen diferentes implementaciones para su propio controlador paso a paso y el indexador que implementa la interfaz IList<T>:

public T Item[int index] { get; } 

T IList<T>.Item[int index] { get; set; } 

Si lanzas su lista de IList<int>, obtendrá un error de ejecución en lugar del error de compilación:

((IList<int>)b)[2] = 3; 

Edición:
Para poner en práctica el indexador en su propia clase, se utiliza la palabra clave this:

public T this[int index] { get { ... } } 

T IList<T>.this[int index] { get { ... } set { ... } } 
+0

Esa fue mi idea también, pero cuando intento eso en mi propia clase implementando IList , no compilará –

+0

¿Cuál es el mensaje de error de compilación? – thecoop

+0

@EsbenP: para implementarlo en una clase, la sintaxis no es la misma que la firma que se muestra en la documentación. Ver la edición de arriba. – Guffa