2013-03-19 1 views
10

mi piacerebbe essere in grado di fare qualcosa del genere:Perché non è possibile utilizzare un tipo concreto compatibile in sede di attuazione di un'interfaccia

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace Test 
{ 
    public interface IFoo 
    { 
     IEnumerable<int> integers { get; set; } 
    } 

    public class Bar : IFoo 
    { 
     public List<int> integers { get; set; } 
    } 
} 

Perché il compilatore si lamenta ..?

Error 2 'Test.Bar' does not implement interface member 'Test.IFoo.integers'. 'Test.Bar.integers' cannot implement 'Test.IFoo.integers' because it does not have the matching return type of 'System.Collections.Generic.IEnumerable<int>'. 

Capisco che l'interfaccia IEnumerable dice e la classe utilizza un elenco, ma una lista è un IEnumerable .....

cosa posso fare? Non voglio specificare IEnumerable nella classe, voglio usare un tipo concreto che implementa IEnumerable, come Elenco ...

+2

"Voglio utilizzare un tipo di cemento che implementa IEnumerable , come Elenco ... "- perché? – jcollum

risposta

12

Questo è un problema di Covarianza/controvarianza di tipo (vedere http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#C.23).

C'è una soluzione: utilizzare le interfacce esplicite, in questo modo:

public class Bar : IFoo { 

    private IList<int> _integers; 

    IEnumerable<int> IFoo.integers { 
     get { return _integers }; 
     set { _integers = value as IList<int>; } 
    } 

    public IList<int> integers { 
     get { return _integers; } 
     set { _integers = vale; } 
    } 
} 

Nota che integers dovrebbe essere TitleCased per conformarsi alle linee guida di .NET.

Speriamo che possiate vedere il problema nel codice sopra: IList<int> è compatibile con IEnumerable<int> solo per l'accessor, ma non per l'impostazione. Cosa succede se qualcuno chiama IFoo.integers = new Qux<int>() (dove Qux : IEnumerable<int> ma nonQux : IList<int>).

+0

Questo risulta estremamente utile quando si affronta il problema della deserializzazione di oggetti JSON in un oggetto concreto che deve avere parametri di interfaccia. Fornendo anche i parametri concreti, il deserializzatore può trovare un modo per istanziare un oggetto per i parametri. –

4

Sebbene List implementa IEnumerable non è il modo in cui le interfacce funzionano. L'interfaccia specifica esattamente quali tipi devono essere esposti per le proprietà. Se è stato creato un'interfaccia generica come

public interface IFoo<T> where T : IEnumerable<int> 
{ 
    T integers { get; set; } 
} 

È quindi possibile utilizzare IFoo<List<int>> per la sua attuazione nel modo che ci si aspetta.

3

Non sarà possibile utilizzare un tipo di calcestruzzo a meno che non lo facciate dietro le quinte. Il problema è che è possibile ottenere e impostare la proprietà.

L'interfaccia specifica che la proprietà è di tipo IEnumerable<int>. HashSet<int> strumenti IEnumerable<int>. Ciò significa che il seguente dovrebbe funzionare bene:

IFoo instance = new Bar(); 
instance.integers = new HashSet<int>(); 

Ma dal momento che si sta cercando di implementare l'interfaccia utilizzando il tipo di cemento List<int>, non c'è modo che l'assegnazione può funzionare.

La soluzione più semplice, a patto che non lo fai costantemente bisogno di ri-assegnare la raccolta, sarebbe solo specificare un getter per la raccolta:

public interface IFoo 
{ 
    IEnumerable<int> Integers { get; } 
} 

public class Bar 
{ 
    public List<int> Integers { get; private set; } 

    public Bar(List<int> list) 
    { 
     Integers = list; 
    } 
} 
+0

Suppongo che funzioni (non riesco a testarlo adesso), ma non capisco perché. Perché rimuovere il setter dall'interfaccia permette a un 'Elenco ' di implementare un 'IEnumerable '? Il compilatore scrive tipicamente in ogni direzione (ad es.potresti implementare un 'Elenco Integers {set; } 'con un' IEnumerable Integers {get privato; set} '? – Bobson

+0

Presumo che la 'Barra' nel codice di esempio implementa' IFoo'? Se è così, il codice non risolve il problema. Stesso errore di compilazione: 'Test.Bar.Integers 'non può implementare Test.IFoo.Integers' perché non ha il tipo di restituzione corrispondente di 'System.Collections.Generic.IEnumerable ''. Mi sto perdendo qualcosa? – Peter