Question

J'ai récemment eu des problèmes en essayant d'ajouter AddRange (IEnumerable) à une liste. Probablement un problème classique, mais je ne l’ai pas encore vraiment compris.

Je comprends que les méthodes qui attendent un paramètre de liste ne sont pas satisfaites d'une liste, car elles pourraient essayer d'ajouter une base à la liste, ce qui est évidemment impossible.

Mais si j’obtiens cela correctement, étant donné que IEnumerables eux-mêmes ne peut pas être modifié, cela devrait fonctionner dans ce cas.

Le code auquel j'ai pensé ressemble à ceci:

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
    }
}

J'ai essayé de transmettre un indice au compilateur dans la méthode AddRange2, mais cela n'a fait que passer au problème.

Ma façon de penser est-elle défectueuse? Est-ce une limitation de la langue ou est-ce que c'est par la conception?

IIRC, la prise en charge de ce type d'opérations a été ajoutée à Java 1.5, alors peut-être sera-t-elle ajoutée à C # ultérieurement ...?

Était-ce utile?

La solution

Il s'agit d'une covariance, qui sera corrigée dans C # 4.0 / .NET 4.0. Pour le moment, l’option générique est la meilleure réponse (pour IEnumerable < T > - et non IList < T > etc ).

Mais dans la méthode générique, vous devez penser en termes de T . Vous pouvez également utiliser Cast < T > ou OfType < T > avec LINQ pour obtenir un résultat similaire.

Autres conseils

En C # 3.0, vous pouvez utiliser la commande "Cast". méthode d'extension. Si vous importez System.Linq puis utilisez ce code:

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

Ensuite, cela devrait fonctionner pour vous.

Solution de contournement avec la méthode d'extension:

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>() );

mais le " < Personne, employé > " est un peu gênant: /.

La solution de conversion peut bien sûr générer des exceptions de conversion de classe. La personne qui a posté le travail d'extension énumérable a dit que c'était maladroit. Je suis venu avec une solution qui est à moitié aussi maladroite, je ne sais pas si je vais l'utiliser:

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);
    }
}

Utilisation:

IEnumerable mammals = UpTo < Mammal > .From (Kennel.Dogs)

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