Est-il erroné d'incarner un énumérateur d'une classe enfant en un énumérateur d'une classe parent?

StackOverflow https://stackoverflow.com/questions/229656

  •  04-07-2019
  •  | 
  •  

Question

J'ai une erreur dans ma construction qui dit:

  

Erreur 12 Impossible de convertir implicitement   type   'System.Collections.Generic.IEnumerator & Lt; BaseClass & Gt; '   à   'System.Collections.Generic.IEnumerator & Lt; IParentClass & Gt; '.   Une conversion explicite existe (êtes-vous   manque un casting?)

Est-ce une erreur de simplement le jeter?

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 la suivante: puis-je changer cette ligne:

return this.GetEnumerator();

à:

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

(sans effets secondaires négatifs)?

Réponse acceptée:
J'ai changé la fonction comme suit (après avoir lu le message de Jon Skeet):

IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
        {
            return this.Map.Values.Cast<IParentClass>().GetEnumerator();
        }
Était-ce utile?

La solution

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 support (pour les délégués et les interfaces) mais il n’est pas encore vraiment utilisé.

Si vous renvoyez IEnumerable<BaseClass> au lieu de IEnumerator<BaseClass> (et en supposant .NEt 3.5), vous pouvez utiliser Enumerable.Cast - mais vous devrez actuellement é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;
    }
}

Sinon, dans votre cas, vous pouvez utiliser la conversion plus tôt:

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

Autres conseils

Non, vous ne pouvez pas, du moins en C # 3.0 et en dessous, la variance d'interface n'est pas prise en charge. Voir l'excellente série d'Eric Lippert à ce sujet, et plus particulièrement celui-ci .

Non, ce n'est pas sans danger, voir ci-dessous:

using System.Collections.Generic; classe Foo {} classe 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 pouvoir 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;
        }
    }
}

Pour expliquer pourquoi ceci n'est pas approprié, décrivez à la place d'un Enumerator, d'un List. Les deux utilisent des génériques - le compilateur ne gère pas l'un ou l'autre de manière particulière 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 correcte - une liste de IParentThing s devrait pouvoir recevoir un ChildThing2. Mais une liste de ChildThing1 s ne peut gérer un List&lt;ChildThing1>, ni même un implémenteur de List&lt;IParent> autre que <=> - en d’autres termes, si le <=> était autorisé à transtyper en <=>, il être capable de gérer toutes toutes les sous-classes de <=>, pas seulement <=> et <=>.

Notez que les génériques Java ont un moyen de dire que & "Je veux une liste de tout ce qui hérite de cette &"; En plus de & «Je veux une liste de tout ce dont cela hérite, &»; ce qui permet des solutions plus intéressantes (et à mon avis élégantes) à certains problèmes.

IEnumerator<BaseClass> et IEnumerator<ParentClass> ne sont pas liés, même si leurs paramètres génériques le sont. Je préférerais utiliser une méthode d’extension LINQ Select comme suit:

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

ou la Cast méthode d'extension:

return this.Cast<IParentClass>().GetEnumerator();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top