É errado lançar um enumerador de uma classe infantil para um enumerador da classe dos pais?

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

  •  04-07-2019
  •  | 
  •  

Pergunta

Eu tenho um erro na minha construção que diz:

O erro 12 não pode implicitamente converter tipo 'System.Collection.Genic.ienumerator <Shaseclass>' para 'System.Collections.Genic.ienumerator <iparentclass>'. Existe uma conversão explícita (você está perdendo um elenco?)

É errado simplesmente expulsá -lo?

Este é o meu 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!
        }

Minha pergunta é: posso apenas mudar esta linha:

return this.GetEnumerator();

para:

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

(sem efeitos colaterais ruins)?

Resposta aceita:
Mudei a função para o seguinte (depois de ler a postagem de Jon Skeet):

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

Solução

Não, você não pode, porque os genéricos não são covariantes em C# no momento. O .NET em si tem algum suporte (para delegados e interfaces), mas ainda não foi usado.

Se você estivesse voltando IEnumerable<BaseClass> ao invés de IEnumerator<BaseClass> (e assumindo .NET 3.5) Você pode usar Enumerable.Cast - mas você precisará escrever seu próprio método de extensão, por exemplo

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

Como alternativa, no seu caso, você pode usar o elenco anteriormente:

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

Outras dicas

Não, você não pode, pelo menos no C# 3.0 e abaixo da variação da interface não é suportada. Veja a excelente série de Eric Lippert sobre isso, e especificamente Este.

Não, não é seguro, veja abaixo:

usando System.Collections.Genic; classe Foo {} Barra de classe: 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
    }
}

No entanto, você deve poder usar um bloco de iterador para fazer o elenco para você?

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

Para a razão Por quê Isso não é apropriado, imagem em vez de um Enumerator, uma List. Ambos usam genéricos - o compilador não lida com nenhum de uma maneira especial em relação a argumentos genéricos.

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

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

Este primeiro método é bom - uma lista de IParentThings deve ser capaz de receber um ChildThing2. Mas uma lista de ChildThing1S não pode lidar com um ChildThing2, ou mesmo qualquer implementador de IParentThing outro que não seja ChildThing1 - Em outras palavras, se o List&lt;ChildThing1> foi permitido lançar como um List&lt;IParent>, teria que ser capaz de lidar com tudo subclasses de IParentThing, não apenas IParentThing e ChildThing1.

Observe que os genéricos da Java têm uma maneira de dizer que "quero uma lista de qualquer coisa que herde disso", além de "Quero uma lista de qualquer coisa que isso herda", que permite soluções mais interessantes (e na minha opinião elegantes) para alguns problemas.

IEnumerator<BaseClass> e IEnumerator<ParentClass> não estão relacionados, apesar de seus parâmetros genéricos serem. Eu usaria um Linq Select Método de extensão como assim:

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

ou o Cast Método de extensão:

return this.Cast<IParentClass>().GetEnumerator();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top