2008-10-23 4 views
3

J'ai une erreur dans ma construction qui dit:Est-ce mal de lancer un énumérateur d'une classe enfant vers un énumérateur d'une classe parente?

Erreur 12 Impossible de convertir implicitement le type 'System.Collections.Generic.IEnumerator < BaseClass>' à « System.Collections.Generic. IEnumerator < IParentClass> '. Une conversion explicite existe (vous manque un casting?)

Est-il tort de simplement le jeter loin?

Ceci est mon code:

public Dictionary<Int32, BaseClass> Map { get; private set; } 

public IEnumerator<BaseClass> GetEnumerator() 
     { 
      return this.Map.Values.GetEnumerator(); 
     } 

public IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator() 
     { 
      return this.GetEnumerator(); // ERROR! 
     } 

Ma question est, je peux juste changer cette ligne:

return this.GetEnumerator(); 

à:

return (IEnumerator<IParentClass>)this.GetEnumerator(); 

(sans mauvais effets secondaires) ?

Accepted Réponse:
J'ai changé la fonction à ce qui suit (après avoir lu le message de Jon Skeet):

IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator() 
     { 
      return this.Map.Values.Cast<IParentClass>().GetEnumerator(); 
     } 

Répondre

5

Non, vous ne pouvez pas, car les génériques ne sont pas covariants en C# pour le moment. .NET lui-même a un certain support (pour les délégués et les interfaces) mais il n'est pas encore vraiment utilisé.

Si vous reveniez IEnumerable<BaseClass> au lieu de IEnumerator<BaseClass> (et en supposant .Net 3.5), vous pouvez utiliser Enumerable.Cast - mais vous aurez besoin actuellement d'écrire votre propre méthode d'extension, par exemple

public static IEnumerator<TParent> Upcast<TParent, TChild> 
    (this IEnumerator<TChild> source) 
    where TChild : TParent 
{ 
    while (source.MoveNext()) 
    { 
     yield return source.Current; 
    } 
} 

En variante, dans votre cas, vous pouvez utiliser plus tôt Cast:

return this.Map.Values.Cast<BaseClass>().GetEnumerator(); 
0

IEnumerator<BaseClass> et IEnumerator<ParentClass> ne sont pas liés, eventhough leurs paramètres génériques sont. Je plutôt utiliser une méthode d'extension LINQ Select comme ceci:

return this.Select(x => (IParentClass)x).GetEnumerator(); 

ou la méthode d'extension Cast:

return this.Cast<IParentClass>().GetEnumerator(); 
+0

Impossible de faire avec IEnumerator - vous avez besoin de IEnumerable. (Dans ce cas, utilisez simplement Cast() en premier lieu :) –

+0

Jon m'a battu ... mais encore une fois ... –

+0

J'ai corrigé l'exemple. J'ai également inclus la méthode Cast(). – spoulson

2

Non, vous ne pouvez pas, au moins en C# 3.0 et au-dessous la variance de l'interface est pas prise en charge. Voir l'excellente série d'Eric Lippert sur ce sujet, et plus précisément this one.

0

Non, ce n'est pas sûr, voir ci-dessous:

utilisant System.Collections.Generic; class Foo {} class Bar: Foo {}

static class Program 
{ 
    static IEnumerator<Foo> GetBase() { 
     yield return new Foo(); 
     yield return new Bar(); 
    } 
    static IEnumerator<Bar> GetDerived() 
    { 
     return (IEnumerator<Bar>)GetBase(); 
    } 
    static void Main() 
    { 
     var obj = GetDerived(); // EXCEPTION 
    } 
} 

Cependant, vous devriez être en mesure d'utiliser un bloc itérateur pour faire le casting pour vous?

static IEnumerator<Bar> GetDerived() 
{ 
    using (IEnumerator<Foo> e = GetBase()) 
    { 
     while (e.MoveNext()) 
     { 
      // or use "as" and only return valid data 
      yield return (Bar)e.Current; 
     } 
    } 
} 
0

Raisonner pourquoi cela ne convient pas, l'image au lieu d'un Enumerator, un List. Les deux utilisent des génériques - le compilateur ne gère pas l'un ou l'autre d'une manière spéciale par rapport aux arguments génériques.

void doStuff() { 
    List<IParentThing> list = getList(); 
    list.add(new ChildThing2()); 
} 

List<IParentThing> getList() { 
    return new List<ChildThing1>(); //ERROR! 
} 

Cette première méthode est très bien - une liste de IParentThing s devrait être en mesure de recevoir un ChildThing2. Mais la liste des ChildThing1 s ne peut pas gérer un ChildThing2, ou encore une implementor de IParentThing autre que ChildThing1 - autrement dit, si le List&lt;ChildThing1> a été autorisé à jeter un List&lt;IParent>, il devrait être en mesure de traiter toutes sous-classes de IParentThing, pas seulement IParentThing et ChildThing1. Notez que les génériques Java ont un moyen de dire que "Je veux une liste de tout ce qui en hérite" en plus de "Je veux une liste de tout ce qui hérite," ce qui permet de plus intéressant (et dans mon opinion élégante) des solutions à certains problèmes.

Questions connexes