2009-02-25 9 views
5

Ho una routine diC# passando una collezione di oggetti InterfaceImplementingClass ad una routine che prende una raccolta di interfaccia .NET oggetti

public void SomeRoutine(List<IFormattable> list) { ... } 

Allora provo a chiamare questa routine

List<Guid>list = new List<Guid>(); 
list.Add(Guid.NewGuid()); 
SomeRoutine(list); 

E viene a mancare con un errore in fase di compilazione. System.Guid implementa IFormattable, ma l'errore che ottengo è

non può convertire da 'System.Collections.Generic.List' a 'System.Collections.Generic.List'

NOTA : Otterrai lo stesso errore se utilizzi solo una serie di Guids. Generici NON è la causa ....

Ma! Dato questo

public void SomeRoutine2(IFormattable obj) { ... } 

e questo

Guid a = Guid.NewGuid(); 
SomeRoutine2(a); 

Compila! Quindi la domanda è PERCHÉ? Perché sono in grado di passare un oggetto Guid (che implementa IFormattable) in una routine che accetta un oggetto di IFormattable, ma quando provo ad estenderlo a una raccolta (un elenco generico, o un array o qualsiasi altra cosa), ottengo un errore di conversione?

Ho avuto un momento di trovare una risposta, e ho pensato che questo sarebbe il posto migliore dove andare.

+0

Ti rendi conto che la risposta contrassegnata (covarianza .NET 4.0) non si applica effettivamente agli elenchi, dove-come generici funziona * ora *? –

+0

Vedere anche: http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html –

+0

Tranne che non ha funzionato con una serie di Guids con una routine che sta cercando una serie di IFormattable, sia ....? – emkayultra

risposta

8
+0

La covarianza C# 4.0 non si applica alle classi concrete (solo alle interfacce) e non si applica alle liste (solo IEnumerable - o cose che sono pure "out") –

+0

Cosa intendi esattamente quando dici "puro fuori "? – BFree

+0

Intendo, "IEnumerable " e "IEnumerator " hanno solo un utilizzo "out", ad esempio "T Current {get;}". Contrassegna le cose come "in" o "out" che abilita la co/contro-varianza; ma funziona solo con uno ** o ** l'altro. Non è possibile avere un utilizzo "in" e "out" (quali elenchi richiederebbero). Correzione –

2

Hai provato dichiarando list come oggetto invece di un List<Guid>List<IFormattable>? Questo dovrebbe farti superare l'errore di compilazione.

+0

I generici sono un'opzione molto più bella ... –

3

Il problema è che un elenco <IFormattable> non è solo una raccolta da cui è possibile leggere alcuni IFormattables, è anche una raccolta a cui è possibile aggiungere IFormattables. Un elenco <Guida> soddisfa il primo requisito, ma non il secondo. E se fosse SomeRoutine

public void SomeRoutine(List<IFormattable> list) 
{ 
    list.Add(5); 
} 

Questo è un Int32 IFormattable, in modo che il metodo dovrebbe essere in grado di aggiungerlo alla lista <IFormattable> che chiedeva. Non funzionerà se il compilatore ti consente di passare il tuo elenco <Guid>.

Le nuove funzionalità di C# 4.0 a cui fa riferimento BFree consentiranno di dire al compilatore quando queste cose sono sicure.Si può dire sia

  • Anche se ho chiesto un riferimento IFormattable, lo leggerò solo. Se è davvero un riferimento Guid, posso tranquillamente trattarlo come un IFormattable e non cercherò di assegnargli un Int32.
  • Anche se ho chiesto un riferimento IFormattable, scriverò solo su di esso. Se è veramente un riferimento all'oggetto, posso tranquillamente assegnare il mio IFormattable a questo, e non importa se il valore corrente non è un IFormattable perché non lo leggerò.
+0

C# 4.0 covariance doesn ' t si applicano a classi concrete (solo per interfacce) e non si applicano alle liste (solo IEnumerable - o cose che sono puramente "out" –

+0

correzione; interfacce + delegati; ma generici è ancora una risposta migliore qui ... –

+0

Ovviamente una lista non può fare nessuna delle asserzioni "in" o "out" consentite da C# 4.0 perché è ancora necessario essere in grado sia di inserire che di recuperare T, quindi non si sta meglio. +1 per la tua risposta generica. – stevemegson

5

Questa è una classica custodia per uso generico; provare:

public static void SomeRoutine<T>(IList<T> list) where T : IFormattable 
{ ... } 

Ora all'interno SomeRoutine si ha accesso a tutti i IFormattable membri, ma che possa funzionare con:

List<Guid>list; ... 
SomeRoutine(list); // note no need to specify T 

Edit: Ho inoltre blogged sulle differenze tra 4,0 covarianza e generici per questo scenario.

Problemi correlati