Frage

Wenn ich diese beiden Klassen:

class A {}
class B : A {}

und ich mache eine Liste<A> , aber ich möchte hinzufügen, eine Liste<B> durch aufrufen-Liste<A>.AddRange(List<B>), aber der compiler weigert sich:

Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>

was ich völlig verstehen, weil IEnumerable<B> erbt nicht von IEnumerable<A>, die Allgemeine Typ ist die Vererbung.

Meine Lösung ist zum Durchlaufen der Liste<B> und individuell fügen Sie Elemente, weil Liste<A>.Add(Element) wird die Arbeit mit den B-Elementen:

foreach(B item in listOfBItems)
{
    listOfAItems.Add(item);
}

Das ist jedoch eher nicht-expressiven, denn was ich will, ist nur AddRange.

Ich könnte verwenden

List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});

aber das ist unnötig verworren und irreführend, denn ich bin nicht konvertieren, bin ich casting .

Frage:Wenn ich Schreibe meine eigene Liste-wie die Sammlung, welche Methode sollte ich hinzufügen, dass würde mir erlauben, zu kopieren, eine Sammlung von B ist in einer Auflistung von A als ein one-liner akin to Liste<A>.AddRange(List<B>) und retain maximum type safety.(Und durch die maximale meine ich, dass das argument ist sowohl eine Auflistung und geben Sie inhertance überprüfen.)

War es hilfreich?

Lösung

In der Tat, generische Typen sind nicht die Variante rechts jetzt.In C# 4.0, IEnumerable<B> wird convertible IEnumerable<A> wenn B ist umwandelbar zu Einem über eine Referenz Umstellung.Für einige details, die das design dieser Funktion finden Sie unter:

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

Andere Tipps

Dies gilt unfortnuately nicht funktionieren, weil Generika in .net (noch) nicht unterstützt Kovarianz.

Sie können ein kleines Helfer-Methode oder Klasse zu überwinden dieses Problem jedoch.

Wenn Sie bei der Umsetzung Ihrer eigenen Liste-Klasse, die Sie hinzufügen können Kovarianz zusätzliche Verwendung eines generischen parameter:

class MyList<T> {
    void AddRange<U>(IEnumerable<U> items) where U: T {
        foreach (U item in items) {
            Add(item);
        }
    }
}

Können Sie nicht einfach tun:

listOfAItems.AddRange(listOfBItems.Cast<A>());

Ich war in der Lage, dies zu erreichen, ist die Verwendung von LINQ...

listOfAItems.AddRange(listOfBItems.Cast<A>());

In Fall, Sie finden sich in einer situation, wo generische Typen sind nicht die Variante, die folgende Erweiterungsmethode Ihr Leben leichter machen kann:

public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList {
    foreach(TOther e in collection) {
        list.Add(e);
    }
}

Anstatt die Ableitung von List<T> oder haben diese Methode in einigen utility-Klasse, wobei es als extension-Methode vereinfacht die Nutzung.Profitieren auch Sie von Inferenz, sodass diese ehemals Ungültiger Aufruf gültig, ohne jede änderung:

List<Animal> animals;
List<Dog> dogs;
animals.AddRange(dogs);

Das einzige, was ich mit oben kommen kann ist dies

public class MyList<T> : List<T>
{
    public void AddRange<Tother>(IEnumerable<Tother> col)
        where Tother: T
    {    
        foreach (Tother item in col)
        {
            this.Add(item);
        }
    }
}

Nannte es bedeutet zu tun, MyList<A>.AddRange<B>(MyList<B>).Dies schlägt fehl, wenn das argument ist nicht enumerable oder wenn die Art der Vererbung funktioniert nicht so, es erfüllt meine Frage ist maximale Typ Sicherheit Anforderung.

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