2009-09-11 9 views
11

Avere questo codice ...C'è qualcosa di magico in ReadOnlyCollection

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

ottengo un errore di compilazione in seconda linea. Mi aspetto un errore di runtime dal ReadOnlyCollection<T> agli attrezzi IList<T> e allo this[T] un setter nell'interfaccia IList<T>.

Ho provato a replicare la funzionalità di ReadOnlyCollection, ma la rimozione del setter da this[T] è un errore di compilazione.

risposta

16

Il indexer è implementato con esplicito implementazione dell'interfaccia, così sarete solo in grado di accedervi se si fa:

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; 

Naturalmente, sarà poi riuscire in fase di esecuzione ...

Ciò è del tutto intenzionale e disponibile - vuol dire che quando il compilatore sa è un ReadOnlyCollection, i bit non supportate di funzionalità non sono disponibili a te, aiutandoti a distoglierti dal tempo di esecuzione.

È un passaggio interessante e relativamente inusuale, efficacemente che implementa implicitamente metà di una proprietà/indicizzatore e una metà esplicitamente.

Contrariamente alle mie pensieri precedenti, credo ReadOnlyCollection<T>realtà implementa l'intera indicizzatore in modo esplicito, ma anche fornisce un indicizzatore pubblico in sola lettura.In altre parole, si tratta di qualcosa di simile:

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

Ok, ma come faccio a replicare la funzionalità di ReadOnlyCollection utilizzando l'implementazione esplicita. Non vedo come sia possibile rimuovere un metodo o una proprietà dall'interfaccia. –

+0

@EsbenP: non è possibile rimuovere un metodo dall'interfaccia ... ma è possibile renderlo disponibile solo quando il tipo statico del riferimento è l'interfaccia anziché la classe che implementa l'interfaccia. –

+0

Ok, se ho due indicizzatori, uno di loro attuazione IList esplicitamente funziona T IList .questo [int index] { ottenere { fonte di ritorno [index]; } set { throw new NotImplementedException(); }} pubblica T questo [int index] { ottenere { fonte di ritorno [index]; } } –

2

Implementa esplicitamente IList.Items, che lo rende non pubblico, e dovrete eseguire il cast sull'interfaccia per raggiungere la sua implementazione e implementare un nuovo [...] indicizzatore, che viene invece utilizzato, che ha solo un get-accessor.

Se si esegue il cast della raccolta su IList, il codice verrà compilato, ma in runtime non verrà eseguito.

Purtroppo non so come fare questo in C#, dal momento che la scrittura di un indicizzatore in C# comporta l'uso della parola chiave this, e non si può scrivere questo:

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

@Lasse: Si può scrivere che - con le implementazioni appropriate. Il problema è che non è possibile implementare una metà esplicitamente e una metà implicitamente, per quanto posso dire. –

+0

Non è necessario, se è possibile scrivere ciò, si scrive il setter con il lancio di un'eccezione e quindi si implementa un pubblico [...] questo indicizzatore con solo il getter. –

1

Non c'è magia, il ReadOnlyCollection basta avere implementazioni differenti per essa la propria indicizzatore e l'indicizzatore che implementa l'interfaccia IList<T>:

public T Item[int index] { get; } 

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

Se lanci la vostra lista per IList<int>, si otterrà un errore di runtime invece che l'errore di compilazione:

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

Edit:
Per implementare l'indicizzatore nella propria classe, si utilizza la parola chiave this:

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

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

Questo è stato anche il mio pensiero, ma quando provo che nella mia classe implementerò IList non verrà compilato –

+0

Qual è il messaggio di errore compilato? – thecoop

+0

@EsbenP: per implementarlo in una classe la sintassi non è la stessa della firma mostrata nella documentazione. Vedi la modifica sopra. – Guffa