Comment convertir correctement une classe en classe abstraite lors de l’utilisation de génériques de type?

J’ai les cours suivants

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

J’ai une méthode qui ressemble à ceci (simplifié)

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

Quand j’appelle cela en utilisant une instance de LoginPresenter

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

La ligne Activator.CreateInstance(viewType) résout correctement en une nouvelle instance de mon LoginView . Toutefois, le control as BaseView ne peut pas effectuer correctement la control as BaseView et renvoie donc null .

Existe-t-il un moyen de BaseView correctement le control dans BaseView sans utiliser de génériques de type spécifiques?

Étant donné que LoginView hérite de BaseView et LoginPresenter de BaseViewPresenter , je suppose qu’il existe un moyen de convertir LoginView en BaseView .

Je suis coincé avec l’aide de .Net 3.5

C’est une question très fréquemment posée. Renommons vos types:

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

Votre question est maintenant:

J’ai un BowlOfApples , qui hérite de FruitBowl . Pourquoi ne puis-je pas l’utiliser comme FruitBowl ? Une pomme est un fruit, alors un bol de pommes est un bol de fruits.

Non ce n’est pas. Vous pouvez mettre une banane dans un bol de fruits, mais vous ne pouvez pas placer une banane dans un bol de pommes . Par conséquent, un bol de pommes n’est pas un bol de fruits. (Et par un argument similaire, un bol de fruits n’est pas non plus un bol de pommes.) Les opérations que vous pouvez effectuer légalement sur les deux types étant différentes , elles ne peuvent pas être compatibles .

Voici une photo de la légende de StackOverflow, Jon Skeet, démontrant ce fait:

entrez la description de l'image ici

La fonctionnalité souhaitée s’appelle une contravariance générique . Elle est prise en charge uniquement sur les interfaces et les types delegates lorsque le compilateur peut prouver que la variance est sûre et que le type variable est un type référence. Par exemple, vous pouvez utiliser un IEnumerable dans un contexte où IEnumerable est nécessaire car le compilateur peut vérifier qu’il est impossible de placer une Banana dans une séquence de fruits.

Effectuez une recherche sur “covariance et contravariance C #” sur ce site ou sur le Web et vous y trouverez de nombreux autres détails sur le fonctionnement de cette fonctionnalité. En particulier, ma série d’articles sur la manière dont nous avons conçu et implémenté cette fonctionnalité en C # 4 commence ici: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in -c-part-one.aspx

J’ai accepté la réponse d’Eric car elle fournit une excellente explication de la raison pour laquelle ce que je voulais était impossible, mais j’ai aussi pensé partager ma solution au cas où quelqu’un d’autre rencontrerait le même problème.

J’ai supprimé le paramètre de type générique de ma classe BaseView origine et créé une deuxième version de la classe BaseView qui incluait le paramètre de type générique et ses spécificités.

La première version est utilisée par ma méthode .Resolve() ou par un autre code ne se souciant pas des types spécifiques, et la deuxième version est utilisée par tout code attentionné, tel que l’implémentation d’un BaseView

Voici un exemple de la recherche de mon code

 // base classes 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 classes 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; } 

Vous vous attendez à traiter le type comme étant covariant par rapport à l’argument générique. Les classes ne peuvent jamais être covariantes; vous auriez besoin d’utiliser une interface plutôt que (ou en plus) une classe abstraite pour la rendre covariante par rapport à T Vous devez également utiliser C # 4.0.