c # linq `list .Addrange` metodo non funziona
Domanda
Ho un'interfaccia definita come di seguito:
public interface TestInterface{
int id { get; set; }
}
.
e due classi LINQ-to-SQL implementano quell'interfaccia:
public class tblTestA : TestInterface{
public int id { get; set; }
}
public class tblTestB : TestInterface{
public int id { get; set; }
}
.
Ho elenchi IeNumerabili A e B popolati dai record del database da TBLTESTA e TBLTESTB
IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable();
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable();
.
Tuttavia, non è consentito quanto segue:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a);
list.AddRange(b);
.
Devo fare come segue:
foreach(tblTestA item in a)
list.Add(item)
foreach(tblTestB item in b)
list.Add(item)
.
C'è qualcosa che sto sbagliando?Grazie per qualsiasi aiuto
Soluzione
funziona in c # 4, a causa di covarianza generica . A differenza delle versioni precedenti di C #, c'è una conversione da IEnumerable<tblTestA>
a IEnumerable<TestInterface>
.
La funzionalità è stata nella CLR da V2, ma è stata esposta solo in C # 4 (ei tipi di framework non ne ha approfittato prima di .Net 4). IT si applica alle interfacce generiche e ai delegati (non classi) e solo per i tipi di riferimento (quindi non c'è conversione da IEnumerable<int>
a IEnumerable<object>
ad esempio.) Funziona anche solo dove ha senso - IEnumerable<T>
è covariante Gli oggetti vengono solo "fuori" dell'API, mentre IList<T>
è invariante perché è possibile aggiungere valori con quell'aPI anche.
Contravariance generico è supportato anche, lavorando nell'altra direzione - quindi ad esempio è possibile convertire da IComparer<object>
in IComparer<string>
.
Se non stai usando C # 4, quindi il suggerimento di Tim di usare Enumerable.Cast<T>
è buono - perdi una piccola efficienza, ma funzionerà.
Se vuoi saperne di più sulla varianza generica, Eric Lippert ha un Long Series of Blog Post A proposito , e ne ho parlato a NDC 2010 che puoi guardare sul Pagina video NDC .
Altri suggerimenti
Non stai facendo nulla di sbagliato: List<TestInterface>.AddRange
si aspetta un IEnumerable<TestInterface>
.Non accetterà un IEnumerable<tblTestA>
o IEnumerable<tblTestB>
.
I tuoi loops foreach
funzionano.In alternativa, è possibile utilizzare Cast
per modificare i tipi:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
. L'addrange si aspetta un elenco di oggetti di interfaccia, e i tuoi vaiblebles "A" e "B" sono definiti come un elenco di oggetti di classe derivati.Ovviamente, sembra ragionevole che .Net possa logicamente farlo saltare e trattarli come elenchi degli oggetti di interfaccia perché implementano in effetti l'interfaccia, quella logica non è stata semplicemente costruita in 3.5.
Tuttavia, questa abilità (chiamata "Covariacce") è stata aggiunta a .NET 4.0, ma finché non aggiorni a questo, sarete bloccati con looping, o forse prova a chiamare Torreray () e quindi lanciare il risultato a ATaskInterface [], o forse una query LINQ per caso ogni elemento e creare un nuovo elenco, ecc.
a
e b
sono di tipo IEnumerable<tblTestA>
e IEnumerable<tblTestB>
Mentre list.AddRange
richiede che il parametro sia di tipo IEnumerable<TestInterface>