在使用类型generics时如何正确地将类强制转换为抽象类?

我有以下课程

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

我有一个看起来像这样的方法(简化)

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

当我使用LoginPresenter的实例调用它时

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

Activator.CreateInstance(viewType)正确解析为我的LoginView的新实例,但是control as BaseView无法正确执行转换,因此返回null

有没有办法在不使用特定类型generics的情况下将control正确地转换为BaseView

由于LoginViewinheritance自BaseView ,而LoginPresenterinheritance自BaseViewPresenter ,我认为有一种方法可以将LoginView转换为BaseView

我坚持使用.Net 3.5

这是一个非常常见的问题。 让我们重命名你的类型:

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

你现在的问题是:

我有一个BowlOfApples ,它inheritance自FruitBowl 。 为什么我不能将它用作FruitBowl ? 苹果是一种水果,所以一碗苹果就是一碗水果。

不,不是。 你可以把香蕉放在一碗水果里,但你不能把香蕉放在一碗苹果里 ,因此一碗苹果不是一碗水果。 (并且通过类似的论证,一碗水果也不是一碗苹果。)由于你可以在两种类型上合法地执行的操作是不同的 ,它们是不兼容的

这是StackOverflow传奇人物Jon Skeet的照片,展示了这一事实:

在此处输入图像描述

您需要的function称为通用逆变 ,当编译器可以certificate方差是安全的,并且变化类型是引用类型时,它仅在接口委托类型上受支持。 例如,您可以在需要IEnumerable的上下文中使用IEnumerable ,因为编译器可以validation您无法将Banana放入一系列水果中。

在本网站或网站上搜索“C#协方差和逆变”,您将找到有关此function如何工作的更多详细信息。 特别是,关于我们如何在C#4中设计和实现此function的系列文章从这里开始: http : //blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-convvariance-in -C-部分one.aspx

我接受了Eric的回答,因为它提供了一个很好的解释,为什么我想要的是不可能的,但我也认为我会分享我的解决方案以防其他人遇到同样的问题。

我从原始的BaseView类中删除了generics类型参数,并创建了第二个版本的BaseView类,其中包含generics类型参数及其详细信息。

第一个版本由我的.Resolve()方法或其他不关心特定类型的代码使用,第二个版本由任何关注的代码使用,例如BaseView

这是我的代码最终看起来如何的一个例子

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

您期望将类型视为与generics参数相关的协变。 类永远不能协变; 你需要使用一个接口而不是(或除了)一个抽象类来使它与T 。 您还需要使用C#4.0。