Como converter corretamente uma class para uma class abstrata ao usar genéricos de tipo?

Eu tenho as seguintes classs

public abstract class BaseViewPresenter { } public abstract class BaseView : UserControl where T : BaseViewPresenter { } public class LoginPresenter : BaseViewPresenter { } public partial class LoginView : BaseView { } 

Eu tenho um método que se parece com isso (simplificado)

 public BaseView Resolve(BaseViewPresenter model) { var type = model.GetType(); var viewType = _dataTemplates[type]; // Correctly creates BaseView object var control = Activator.CreateInstance(viewType); // Fails to cast as BaseView so returns null return control as BaseView; } 

Quando eu chamo isso usando uma instância do LoginPresenter

 var login = new LoginPresenter(); var ctl = Resolve(login); 

A linha Activator.CreateInstance(viewType) resolve corretamente em uma nova instância do meu LoginView , no entanto, o control as BaseView não pode fazer a control as BaseView corretamente, então retorna null .

Existe uma maneira de converter corretamente o control em BaseView sem usar genéricos de tipo específico?

Como o LoginView BaseView do BaseView e o LoginPresenter do BaseViewPresenter , eu diria que há uma maneira de converter o LoginView para o BaseView .

Eu estou preso com o uso de .net 3.5

Esta é uma pergunta muito freqüente. Vamos renomear seus tipos:

 abstract class Fruit { } // was BaseViewPresenter abstract class FruitBowl where T : Fruit // was BaseView class Apple : Fruit { } // was LoginPresenter class BowlOfApples : FruitBowl { } // was LoginView 

Sua pergunta agora é:

Eu tenho um BowlOfApples , que herda de FruitBowl . Por que não posso usá-lo como um FruitBowl ? Uma maçã é uma fruta, então uma tigela de maçãs é uma tigela de frutas.

Não é não. Você pode colocar uma banana em uma tigela de frutas, mas você não pode colocar uma banana em uma tigela de maçãs e, portanto, uma tigela de maçãs não é uma tigela de frutas. (E por argumento semelhante, uma tigela de frutas também não é uma tigela de maçãs.) Como as operações que você pode executar legalmente nos dois tipos são diferentes , elas não podem ser compatíveis .

Aqui está uma foto da lenda do StackOverflow, Jon Skeet, demonstrando este fato:

insira a descrição da imagem aqui

O recurso que você deseja é chamado de contravariância genérica e é suportado apenas em interfaces e tipos delegates quando o compilador pode provar que a variação é segura e quando o tipo variável é um tipo de referência. Por exemplo, você pode usar um IEnumerable em um contexto onde IEnumerable é necessário porque o compilador pode verificar se não há nenhuma maneira que você pode colocar um Banana em uma seqüência de frutas.

Faça uma pesquisa sobre “covariância e contravariância do c #” neste site ou na Web e você encontrará muito mais detalhes sobre como esse recurso funciona. Em particular, minha série de artigos sobre como projetamos e implementamos esse recurso em C # 4 começa aqui: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in -c-part-one.aspx

Aceitei a resposta de Eric, uma vez que isso fornece uma ótima explicação do motivo pelo qual o que eu queria não era possível, mas também pensei em compartilhar minha solução caso alguém se depare com esse mesmo problema.

Eu removi o parâmetro de tipo genérico da minha class BaseView original e criei uma segunda versão da class BaseView que incluía o parâmetro de tipo genérico e especificidades para ele.

A primeira versão é usada pelo meu método .Resolve() ou outro código que não se importa com os tipos específicos, e a segunda versão é usada por qualquer código que se importe, como a implementação de um BaseView

Veja um exemplo de como meu código acabou parecendo

 // base classs public abstract class BaseViewPresenter { } public abstract class BaseView : UserControl { public BaseViewPresenter Presenter { get; set; } } public abstract class BaseView : BaseView where T : BaseViewPresenter { public new T Presenter { get { return base.Presenter as T; } set { base.Presenter = value; } } } // specific classs public class LoginPresenter : BaseViewPresenter { } public partial class LoginView : BaseView { // Can now call things like Presenter.LoginPresenterMethod() } // updated .Resolve method used for obtaining UI object public BaseView Resolve(BaseViewPresenter presenter) { var type = model.GetType(); var viewType = _dataTemplates[type]; BaseView view = Activator.CreateInstance(viewType) as BaseView; view.Presenter = presenter; return view; } 

Você está esperando tratar o tipo como sendo covariante em relação ao argumento genérico. As classs nunca podem ser covariantes; você precisaria usar uma interface em vez de (ou além de) uma class abstrata para torná-la covariante em relação a T Você também precisa estar usando o C # 4.0.