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 deFruitBowl
. Por que não posso usá-lo como umFruitBowl
? 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:
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.