Question

J'ai une interface définie comme ci-dessous:

public interface TestInterface{
    int id { get; set; }
}

Et deux classes Linq-to-SQL implémentant cette interface :

public class tblTestA : TestInterface{
    public int id { get; set; }
}

public class tblTestB : TestInterface{
    public int id { get; set; }
}

J'ai des listes IEnumerable a et b remplies par les enregistrements de base de données de tblTestA et tblTestB

IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable();
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable();

Toutefois, ce qui suit n'est pas autorisé :

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a);
list.AddRange(b);

Je dois faire comme suit :

foreach(tblTestA item in a)
    list.Add(item)

foreach(tblTestB item in b)
    list.Add(item)

Y a-t-il quelque chose que je fais de mal ?Merci pour toute aide

Était-ce utile?

La solution

Cela fonctionne en C# 4, en raison de covariance générique.Contrairement aux versions précédentes de C#, il existe une conversion de IEnumerable<tblTestA> à IEnumerable<TestInterface>.

La fonctionnalité est présente dans le CLR à partir de la v2, mais elle n'a été exposée qu'en C# 4 (et les types de framework n'en ont pas non plus profité avant .NET 4).Il seulement s'applique aux interfaces génériques et aux délégués (pas aux classes) et uniquement aux types référence (il n'y a donc pas de conversion depuis IEnumerable<int> à IEnumerable<object> par exemple.) Cela ne fonctionne également que là où cela a du sens - IEnumerable<T> est covariant car les objets ne sortent que de l'API, alors que IList<T> est invariant car vous pouvez également ajouter des valeurs avec cette API.

La contravariance générique est également prise en charge, travaillant dans l'autre sens - ainsi, par exemple, vous pouvez convertir depuis IComparer<object> à IComparer<string>.

Si vous n'utilisez pas C# 4, la suggestion de Tim d'utiliser Enumerable.Cast<T> est une bonne solution - vous perdez un peu d'efficacité, mais cela fonctionnera.

Si vous souhaitez en savoir plus sur la variance générique, Eric Lippert a un longue série d'articles de blog à ce sujet, et j'en ai parlé au NDC 2010 que vous pouvez regarder sur Page vidéo NDC.

Autres conseils

Vous ne faites rien de mal : List<TestInterface>.AddRange s'attend à un IEnumerable<TestInterface>.Il n'acceptera pas un IEnumerable<tblTestA> ou IEnumerable<tblTestB>.

Ton foreach les boucles fonctionnent.Alternativement, vous pouvez utiliser Cast pour changer les types :

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());

L'Addrange s'attend à une liste d'objets d'interface, et vos varaibles "A" et "B" sont définies comme une liste d'objets de classe dérivés.De toute évidence, il semble raisonnable que .NET puisse logiquement que le saut et les traite comme des listes d'objets d'interface car ils mettent en œuvre l'interface, cette logique n'était tout simplement pas intégrée à. Net allant jusqu'à 3.5.

Cependant, cette capacité (appelée "Covariance") a été ajoutée à .NET 4.0, mais jusqu'à ce que vous mettriez la mise à niveau, vous serez coincé avec une boucle, ou peut-être essayer d'appeler Toarray () puis de lancer le résultat à unTaskInterface [], ou peut-être une requête LINQ pour traiter chaque élément et créer une nouvelle liste, etc.

a et b sont du type IEnumerable<tblTestA> et IEnumerable<tblTestB>
Alors que list.AddRange exiger que le paramètre soit de type IEnumerable<TestInterface>

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top