Frage

Ich laufe vor kurzem in Schwierigkeiten, als zu AddRange (IEnumerable) auf eine Liste zu versuchen. Wahrscheinlich ein klassisches Problem, aber ich weiß nicht wirklich bekommen noch.

Ich verstehe, dass Methoden, um eine Liste Parameter sind nicht zufrieden mit einer Liste erwartet, weil sie eine Base zu der Liste hinzufügen könnten versuchen, was offensichtlich unmöglich ist.

Aber wenn ich das richtig bekommen, da IEnumerables sich nicht geändert werden können, sollte es in diesem Fall arbeiten.

Der Code Ich dachte an sieht wie folgt aus:

class Foo
{
}

class Bar : Foo
{
}

class FooCol
{
    private List<Foo> m_Foos = new List<Foo> ();

    public void AddRange1(IEnumerable<Foo> foos)
    {
        m_Foos.AddRange (foos); // does work
    }

    public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
    {
        m_Foos.AddRange (foos); // does not work
    }
}

class Program
{
    static void Main(string[] args)
    {
        FooCol fooCol = new FooCol ();

        List<Foo> foos = new List<Foo> ();
        List<Bar> bars = new List<Bar> ();

        fooCol.AddRange1 (foos); // does work
        fooCol.AddRange1 (bars); // does not work

        fooCol.AddRange2 (foos); // does work
        fooCol.AddRange2 (bars); // does work
    }
}

Ich habe versucht, einen Hinweis an den Compiler in der AddRange2 Methode zu übergeben, aber das nur Problem bewegt sich um.

Ist mein Weg fehlerhaft des Denkens? Ist dies eine Einschränkung der Sprache oder ist es durch Design?

IIRC, Unterstützung für diese Art von Operationen zu Java 1.5 wurde hinzugefügt, so vielleicht wird es in der Zukunft zu einem bestimmten Zeitpunkt zu C # hinzugefügt werden, auch ...?

War es hilfreich?

Lösung

Dies ist Kovarianz und C # 4.0 / .NET 4.0 befestigenden in. Vorerst ist die generische Option die beste Antwort (für IEnumerable<T> - nicht IList<T> etc ).

Aber innerhalb der generischen Methode haben Sie in Bezug auf T zu denken. Sie könnten auch Cast<T> oder OfType<T> mit LINQ verwenden, um etwas ähnliches zu erreichen.

Andere Tipps

In C # 3.0 können Sie die "Cast" Extension-Methode verwenden. Wenn Sie importieren System.Linq und dann diesen Code verwenden:

public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
{
    m_Foos.AddRange (foos.Cast<Foo>());
}

Dann sollte es für Sie arbeiten.

Es gibt Abhilfe mit der Erweiterung Methode:

public static IEnumerable<TBase> ToBaseEnumerable<TBase, TDerived>( this IEnumerable<TDerived> items ) where TDerived : TBase {
    foreach( var item in items ) {
        yield return item;
    }
}
...
IEnumerable<Employee> employees = GetEmployees(); //Emplyoee derives from Person
DoSomethingWithPersons( employees.ToBaseEnumerable<Person, Employee>() );

aber die "" ist etwas umständlich. /

Die gegossene Lösung natürlich könnte erzeugten Guss Ausnahmen Klasse. Die Person, die zählbare Verlängerung Arbeit postiert um sagten, es sei umständlich. Ich kam mit einer Lösung auf, die nur halb so umständlich ist, weiß nicht, ob ich es verwenden werden:

public static class UpTo<T>
{
    public static IEnumerable<T> From<F>(IEnumerable<F> source) where F:T
    {
        // this cast is guaranteed to work
        return source.Select(f => (T) f);
    }
}

Verbrauch:

IEnumerable Säugetiere = UpTo .Von (kennel.Dogs)

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