2012-07-06 13 views
8

Ho semplificato e riprodotto il mio problema nel codice qui sotto. L'errore che sto ricevendo è: Argomento 1: impossibile convertire da 'System.Collections.ObjectModel.Collection' a 'System.Collections.ObjectModel.CollectionC# Passare una collezione come una collezione di interfacce

Alcuni background per le decisioni di progettazione che hanno portato a questo problema: Ho creato un servizio web per trattare con "IFruit", ma a causa della natura dei collegamenti SOAP e endpoint, mentre li comprendo, ho creato metodi con implementazioni esplicite di IFruit. Sul piano aziendale, la creazione di un metodo separato per ogni specifica implementazione di frutta causerebbe innanzitutto un sacco di codice duplicato e in secondo luogo accorcerebbe il livello aziendale e di servizio in modo abbastanza stretto che l'accettazione di nuovi tipi di IFruit nel servizio Web richiederebbe anche la modifica del mio codice nel livello aziendale (aggiungendo un'altra copia del codice ridondante). Questo progetto è uno che ho implementato con successo usando Java in passato, ma le interfacce C# sembrano essere abbastanza diverse da lanciarmi. Si prega di avvisare.

public interface IFruit{ 
    public string TakeBite(); 
} 

public Apple : IFruit{ 
    public string TakeBite(){ 
     return "tasty bite of apple"; 
    } 
} 

public void EatFruit(Collection<IFruit> fruits){ 
    foreach(var fruit in fruits){ 
     Console.WriteLine(fruit.TakeBite()); 
    } 
} 

public void EatApples(Collection<Apple> apples){ 
    this.EatFruit(apples); 
} 

risposta

9

Provare a cambiare il metodo di EatFruit accettare IEnumerable<IFruit>

public void EatFruit(IEnumerable<IFruit> fruits) 
{ 
    foreach (var fruit in fruits) 
    { 
     Console.WriteLine(fruit.TakeBite()); 
    } 
} 

L'interfaccia supporta IEnumerable<out T> covarianza dal momento che è contrassegnato con il modificatore out. Collection<T> non è contrassegnato con un tale modificatore.

+0

Grazie. Questo compila.Ora per aggiornare i miei test unitari. – YouGotCSharpInMyJava

1

Questo si chiama covarianza.
Tuttavia, non è possibile con le raccolte mutabili.

In caso contrario, potrebbe scrivere EatFruit

fruits.Add(new Orange()); 

.Net 4 non supporta covarianza per le interfacce Reas soli, in modo da poter cambiare a IEnumerable<IFruit> e funzionerà.

+0

Supponendo che Orange implementa IFruit allora c'è un problema lì? – Charleh

+1

@Charleh: 'fruits' è _actually_ a' Collezione '. Non può contenere un 'arancione'. – SLaks

+0

Pensavo che la frutta fosse una collezione che potesse contenere qualsiasi tipo che implementa IFruit? – Charleh

0

Oltre a utilizzare IEnumerable è possibile digitare controllare la collezione (un po 'orribile ma dovrebbe funzionare):

public void EatApples(Collection<IFruit> fruit) 
    var col = new Collection<IFruit>(); 

    fruit.Where(x => x is Apple).ToList().ForEach(x => col.Add(x)); 

    this.EatFruit(col); 
} 

Anche se mi piacerebbe utilizzare IEnumerable invece - questo è solo un'opzione se non si può refactoring: P

(è come tipo di sicurezza come la fate!)

+0

Farebbe 2x lavori - crea un elenco quindi aggiungi ciascuno alla raccolta. Meglio fare: 'foreach (var x in fruit.OfType ()) {col.Add (x); } '. –

+0

Sì, vero - da nessuna parte ho detto che sarebbe stato performante (e ho menzionato anche l'orrido!): D – Charleh

0

soluzione semplice:

public void EatApples(Collection<Apple> apples){ 
    this.EatFruit(
      new Collection<IFruit>(
       apples.Cast<IFruit>() 
     ) 
    ); 
} 

Soluzione migliore: usa la covarianza introdotta in C# 4: vedi Kevin's risposta