2009-03-11 9 views
5

J'ai récemment rencontré des problèmes lors de la tentative d'ajout de AddRange (IEnumerable) à une liste. Probablement un problème classique, mais je ne l'ai pas encore compris. Je comprends que les méthodes qui attendent un paramètre List ne sont pas satisfaites d'une liste, car elles pourraient essayer d'ajouter une base à la liste, ce qui est évidemment impossible.C# Impossible de convertir IEnumerable <Base> en IEnumerable <Derived>

Mais si je comprends bien, étant donné que IEnumerables ne peut pas être modifié, il devrait fonctionner dans ce cas.

Le code que je pensais 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 passer une allusion au compilateur dans la méthode AddRange2, mais cela vient d'emménager à un problème autour.

Est-ce que ma façon de penser est défectueuse? Est-ce une limitation de la langue ou est-ce intentionnellement? Le support de ce type d'opérations a été ajouté à Java 1.5, donc peut-être sera-t-il ajouté à C# dans le futur, aussi ...?

+0

Dupliquer de nombreuses autres questions, y compris plus récemment http://stackoverflow.com/questions/632399 –

+0

En effet, je suis désolé. Mon google-jutsu a encore besoin d'amélioration. – mafu

+0

Pas de problème. Il serait bon d'avoir une FAQ par-tag, IMO ... –

Répondre

18

Ceci est la covariance, et sera corrigé dans C# 4.0/.NET 4.0. Pour l'instant, l'option générique est la meilleure réponse (pour IEnumerable<T> - not 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 réaliser quelque chose de similaire.

+1

Merci pour le nouveau terme, covariance. Je ne savais pas à propos de l'existence. – Sung

0

Il y a solution avec 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 la "personne <, employé>" est peu maladroit: /.

+0

Cast () serait plus simple ... –

2

En C# 3.0, vous pouvez utiliser la méthode d'extension "Cast". 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.

0

La solution de distribution peut bien sûr générer des exceptions de conversion de classe. La personne qui a posté l'extension enumerable a dit que c'était gênant. je suis venu avec une solution qui est seulement la moitié comme 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:

mammifères IEnumerable = UpTo < mammifères > .De (chenil. Dogs)

Questions connexes