Frage

Ich habe eine Schnittstelle wie folgt definiert:

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

Und zwei Linq-to-SQL-Klassen, die diese Schnittstelle implementieren:

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

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

Ich habe IEnumerable-Listen a und b, die mit den Datenbankeinträgen von tblTestA und tblTestB gefüllt sind

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

Folgendes ist jedoch nicht zulässig:

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

Ich muss wie folgt vorgehen:

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

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

Gibt es etwas, was ich falsch mache?Vielen Dank für jede Hilfe

War es hilfreich?

Lösung

Dies funktioniert aufgrund von C# 4 generische Kovarianz.Im Gegensatz zu früheren Versionen von C# gibt es eine Konvertierung von IEnumerable<tblTestA> Zu IEnumerable<TestInterface>.

Die Funktionalität ist seit Version 2 in der CLR enthalten, wurde aber erst in C# 4 verfügbar gemacht (und die Framework-Typen haben sie auch vor .NET 4 nicht genutzt).Es nur gilt für generische Schnittstellen und Delegaten (keine Klassen) und nur für Referenztypen (es erfolgt also keine Konvertierung von IEnumerable<int> Zu IEnumerable<object> zum Beispiel.) Es funktioniert auch nur dort, wo es Sinn macht - IEnumerable<T> ist kovariant, da Objekte nur „aus“ der API kommen, wohingegen IList<T> Ist invariant weil Sie mit dieser API auch Werte hinzufügen können.

Es wird auch generische Kontravarianz unterstützt, die in die andere Richtung funktioniert – so können Sie beispielsweise von konvertieren IComparer<object> Zu IComparer<string>.

Wenn Sie C# 4 nicht verwenden, empfiehlt Tim die Verwendung Enumerable.Cast<T> ist gut – Sie verlieren ein wenig an Effizienz, aber es wird funktionieren.

Wenn Sie mehr über generische Varianz erfahren möchten, hat Eric Lippert eine lange Reihe von Blogbeiträgen darüber, und ich habe auf der NDC 2010 einen Vortrag darüber gehalten, den Sie hier ansehen können NDC-Videoseite.

Andere Tipps

Du machst nichts falsch: List<TestInterface>.AddRange erwartet eine IEnumerable<TestInterface>.Es wird kein akzeptiert IEnumerable<tblTestA> oder IEnumerable<tblTestB>.

Dein foreach Schleifen funktionieren.Alternativ können Sie auch verwenden Cast Um die Typen zu ändern:

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

Der AddRange erwartet eine Liste von Schnittstellenobjekten, und Ihre "A" und "B" -Arwels sind als Liste der abgeleiteten Klassenobjekte definiert.Offensichtlich erscheint es vernünftig, dass .NET diesen Sprung logisch logisch machen könnte und sie als Listen der Schnittstellenobjekte behandeln, da sie tatsächlich die Schnittstelle implementieren, diese Logik wurde einfach nicht in .NET bis 3.5 aufbauen.

Diese Fähigkeit (namens "Covariarce") wurde jedoch zu .NET 4.0 hinzugefügt, aber bis Sie auf das Upgrade darauf stecken, stecken Sie mit Schleifen, oder vielleicht versuchen Sie, Toarray () anzurufen () und dann das Ergebnis an aTaskInterface [], oder vielleicht eine Linq-Abfrage, um jeden Artikel zu fixieren und eine neue Liste zu erstellen usw.

a Und b sind vom Typ IEnumerable<tblTestA> Und IEnumerable<tblTestB>
Während list.AddRange erfordern, dass der Parameter vom Typ ist IEnumerable<TestInterface>

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top