Ho un problema con il ritorno di raccolta e covarianza e mi chiedevo se qualcuno ha una soluzione migliore.Come gestire la covarianza quando si restituisce la raccolta in C#?
Lo scenario è questo:
Ho 2 versione di attuazione e vorrei mantenere l'implementazione versione completamente separato (anche se può avere la stessa logica). Nell'implementazione, vorrei restituire un elenco di elementi e quindi nell'interfaccia, vorrei restituire un elenco di interfaccia dell'articolo. Tuttavia, nell'attuale implementazione dell'interfaccia, vorrei restituire l'oggetto concreto dell'oggetto. Nel codice, sembra qualcosa del genere.
interface IItem
{
// some properties here
}
interface IResult
{
IList<IItem> Items { get; }
}
Quindi, ci sarebbero 2 spazi dei nomi che ha un'implementazione concreta di queste interfacce. Ad esempio,
Namespace Version1
class Item : IItem
class Result : IResult
{
public List<Item> Items
{
get { // get the list from somewhere }
}
IList<IItem> IResult.Items
{
get
{
// due to covariance, i have to convert it
return this.Items.ToList<IItem>();
}
}
}
Ci sarà un'altra implementazione della stessa cosa sotto namespace Version2.
Per creare questi oggetti, ci sarà uno stabilimento che prende la versione e crea il tipo di calcestruzzo appropriato secondo necessità.
Se il chiamante conosce la versione esatta e fa seguito, il codice funziona bene
Version1.Result result = new Version1.Result();
result.Items.Add(//something);
Tuttavia, vorrei che l'utente sia in grado di fare qualcosa di simile.
IResult result = // create from factory
result.Items.Add(//something);
Ma, perché è stato convertito in un altro elenco, il componente aggiuntivo non farà nulla perché l'elemento non verrà aggiunto di nuovo all'oggetto risultato originale.
mi viene in mente un paio di soluzioni come:
- ho potuto sincronizzare i due elenchi, ma che sembra essere un lavoro extra da fare
- ritorno IEnumerable invece di IList e aggiungere un metodo per creare/eliminare le raccolte
- creare una collezione personalizzata che prende il TConcrete e tInterfaccia
capisco perché questo sta accadendo (a causa di sicurezza e tutto il tipo), ma nessuno di workaroun Penso che possa sembrare molto elegante. Qualcuno ha soluzioni o suggerimenti migliori?
Grazie in anticipo!
Aggiornamento
Dopo pensando a questo di più, penso di poter effettuare le seguenti operazioni:
public interface ICustomCollection<TInterface> : ICollection<TInterface>
{
}
public class CustomCollection<TConcrete, TInterface> : ICustomCollection<TInterface> where TConcrete : class, TInterface
{
public void Add(TConcrete item)
{
// do add
}
void ICustomCollection<TInterface>.Add(TInterface item)
{
// validate that item is TConcrete and add to the collection.
// otherwise throw exception indicating that the add is not allowed due to incompatible type
}
// rest of the implementation
}
allora posso avere
interface IResult
{
ICustomCollection<IItem> Items { get; }
}
then for implementation, I will have
class Result : IResult
{
public CustomCollection<Item, IItem> Items { get; }
ICustomCollection<TItem> IResult.Items
{
get { return this.Items; }
}
}
in questo modo, se il chiamante è accedendo alla classe Result, passerà attraverso CustomCollection.Add (TConcrete item) che è già TConcrete.Se il chiamante accede attraverso l'interfaccia IResult, passerà attraverso customCollection.Add (TInterface item) e la convalida avverrà e assicurerà che il tipo sia effettivamente TConcrete.
Farò una prova e vedere se questo funzionerebbe.
Penso che andare con l'opzione # 2 richieda la minor quantità di codice. Ridurrà inoltre l'area della superficie dell'interfaccia, poiché l'implementazione non sarà responsabile del supporto completo di IList, che probabilmente i chiamanti non hanno bisogno. –
La tua soluzione sembra buona, ma perché usi 'ICustomCollection' e non 'ICollection ' direttamente? –
svick
Io totalmente potrei.L'unica ragione è che ho alcuni metodi specifici per la raccolta che non sono disponibili per la normale ICollection a cui è necessario accedere tramite codice interno, che ho dimenticato di menzionare. –
Khronos