¿Está mal convertir un enumerador de una clase secundaria en un enumerador de una clase principal?

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

  •  04-07-2019
  •  | 
  •  

Pregunta

Tengo un error en mi compilación que dice:

El error 12 no puede convertir implícitamente el tipo 'system.collections.generic.ienumerator <SeClass>' a 'system.collections.generic.ienumerator <iParentClass>'.Existe una conversión explícita (¿te estás perdiendo un elenco?)

¿Está mal simplemente desecharlo?

Este es mi código:

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

Mi pregunta es, ¿puedo cambiar esta línea?

return this.GetEnumerator();

a:

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

(sin efectos secundarios negativos)?

Respuesta aceptada:
Cambié la función a la siguiente (después de leer la publicación de Jon Skeet):

IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
        {
            return this.Map.Values.Cast<IParentClass>().GetEnumerator();
        }
¿Fue útil?

Solución

No, no puedes, porque los genéricos no son covariantes en C # en este momento. .NET en sí tiene cierto soporte (para delegados e interfaces) pero aún no se usa realmente.

Si regresaba IEnumerable<BaseClass> en lugar de IEnumerator<BaseClass> (y suponiendo .NEt 3.5) podría usar Enumerable.Cast, pero actualmente necesitará escribir su propio método de extensión, por ejemplo

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

Alternativamente, en su caso, podría usar Cast antes:

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

Otros consejos

No, no puede, al menos en C # 3.0 y la varianza de interfaz inferior no es compatible. Vea la excelente serie de Eric Lippert sobre esto, y específicamente este .

No, no es seguro, ver a continuación:

usando System.Collections.Generic; clase Foo {} barra de clase: 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
    }
}

Sin embargo, ¿debería poder usar un bloque iterador para hacer el lanzamiento por usted?

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

Razonar por qué Esto no es apropiado, imagen en lugar de una. Enumerator, a List.Ambos usan genéricos: el compilador no maneja ninguno de ellos de manera especial en relación con argumentos genéricos.

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

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

Este primer método está bien: una lista de IParentThings debería poder recibir un ChildThing2.Pero una lista de ChildThing1No podemos manejar un ChildThing2, o incluso cualquier implementador de IParentThing otro que ChildThing1 - en otras palabras, si el List&lt;ChildThing1> se le permitió emitir como un List&lt;IParent>, tendría que ser capaz de hacer frente a todo subclases de IParentThing, No solo IParentThing y ChildThing1.

Tenga en cuenta que los genéricos de Java tienen una forma de decir "Quiero una lista de todo lo que hereda de esto" además de "Quiero una lista de todo lo que esto hereda", lo que permite soluciones más interesantes (y en mi opinión elegantes). a algunos problemas.

IEnumerator<BaseClass> y IEnumerator<ParentClass> no están relacionados, aunque sus parámetros genéricos sí lo están.En su lugar usaría un LINQ Select método de extensión así:

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

o el Cast método de extensión:

return this.Cast<IParentClass>().GetEnumerator();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top