Como a covariância e contra-variância genéricas são implementadas no C # 4.0?


106

Não participei do PDC 2008, mas ouvi algumas notícias de que o C # 4.0 foi anunciado para oferecer suporte à covariância e contra-variância genérica. Ou seja, List<string>pode ser atribuído a List<object>. Como poderia ser?

No livro C # in Depth de Jon Skeet , é explicado por que os genéricos C # não suportam covariância e contra-variância. É principalmente para escrever código seguro. Agora, o C # 4.0 mudou para suportá-los. Isso traria o caos?

Alguém conhece os detalhes sobre C # 4.0 pode dar alguma explicação?


Aqui está um bom artigo que cobre as próximas implementações de covariância e contra-variância em delegados e interfaces em C # 4.0: LINQ Farm: Covariância e Contravariância em C # 4.0
CMS

Anders Noråse explica em C # 4.0 - Covariância e contra-variância o conceito e mostra que já é suportado hoje em IL desde .NET 2.0.
Thomas Freudenberg

Respostas:


155

A variação será suportada apenas de forma segura - na verdade, usando as habilidades que o CLR já possui. Portanto, os exemplos que dou no livro de tentar usar um List<Banana>como List<Fruit>(ou seja o que for) ainda não funcionarão - mas alguns outros cenários funcionarão.

Em primeiro lugar, ele só terá suporte para interfaces e delegados.

Em segundo lugar, requer que o autor da interface / delegado decore os parâmetros de tipo como in(para contravariância) ou out(para covariância). O exemplo mais óbvio é IEnumerable<T>aquele que só permite que você retire valores "fora" dele - não permite que você adicione novos. Isso vai se tornar IEnumerable<out T>. Isso não prejudica a segurança de tipo, mas permite que você retorne um IEnumerable<string>de um método declarado para retornar, IEnumerable<object>por exemplo.

A contravariância é mais difícil de dar exemplos concretos de uso de interfaces, mas é fácil com um delegado. Considere Action<T>- isso representa apenas um método que recebe um Tparâmetro. Seria bom ser capaz de converter perfeitamente e usar um Action<object>como um Action<string>- qualquer método que receba um objectparâmetro funcionará bem quando for apresentado com um string. Obviamente, C # 2 já tem covariância e contravariância de delegados até certo ponto, mas por meio de uma conversão real de um tipo de delegado para outro (criando uma nova instância) - consulte P141-144 para exemplos. C # 4 tornará isso mais genérico e (acredito) evitará a criação de uma nova instância para a conversão. (Em vez disso, será uma conversão de referência.)

Espero que isso esclareça um pouco - por favor, me avise se não fizer sentido!


3
Portanto, isso significa que se a classe for declarada como "List <out T>", ela NÃO deve ter uma função de membro como "void Add (T obj)"? O compilador C # 4.0 reportará um erro nisso, certo?
Morgan Cheng

1
Morgan: Esse é certamente o meu entendimento, sim.
Jon Skeet

4
mais uma vez, uma de suas respostas aqui no SO me ajudou imediatamente a melhorar alguns códigos. Obrigado!
Marcar

@ Ark-kun: Sim, estou ciente disso. Portanto, o "ainda não funciona" na mesma frase. (E também estou ciente das razões.)
Jon Skeet

@JonSkeet É correto que "você só pode usar um List<Banana>como IList<Fruit>", como disse @Ark-kun? Se sim, como isso é possível, embora o parâmetro de tipo da IList<T>interface não seja definido como covariante (não out T, mas simplesmente T).
gehho

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.