Est-il erroné d'incarner un énumérateur d'une classe enfant en un énumérateur d'une classe parent?
-
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();
}
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<ChildThing1>
, ni même un implémenteur de List<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();