为了账号安全,请及时绑定邮箱和手机立即绑定

使用类型泛型时如何正确地将类转换为抽象类?

使用类型泛型时如何正确地将类转换为抽象类?

月关宝盒 2019-11-19 10:07:01
我有以下课程public abstract class BaseViewPresenter { }public abstract class BaseView<T> : UserControl    where T : BaseViewPresenter { }public class LoginPresenter : BaseViewPresenter { }public partial class LoginView : BaseView<LoginPresenter> {  }我有一个看起来像这样的方法(简化)public BaseView<BaseViewPresenter> 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<BaseViewPresenter> so returns null    return control as BaseView<BaseViewPresenter>;}当我使用LoginPresenter实例调用此函数时var login = new LoginPresenter();var ctl = Resolve(login);该行Activator.CreateInstance(viewType)正确解析为我的新实例LoginView,但是control as BaseView<BaseViewPresenter>无法正确进行强制转换,因此返回null。有没有一种方法可以正确地强制control转换为BaseView<BaseViewPresenter>不使用特定类型的泛型?由于LoginView继承自BaseView<LoginPresenter>和LoginPresenter继承BaseViewPresenter,我假设有一种方法可以转换LoginView为BaseView<BaseViewPresenter>。我坚持使用.Net 3.5
查看完整描述

3 回答

?
神不在的星期二

TA贡献1963条经验 获得超6个赞

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


abstract class Fruit { }                    // was BaseViewPresenter

abstract class FruitBowl<T> where T : Fruit // was BaseView

class Apple : Fruit { }                     // was LoginPresenter

class BowlOfApples : FruitBowl<Apple> {  }  // was LoginView

您现在的问题是:


我有一个BowlOfApples,继承自FruitBowl<Apple>。为什么不能将其用作FruitBowl<Fruit>?苹果是一种水果,所以一碗苹果是一碗水果。


不,不是。您可以将香蕉放在一碗水果中,但是不能将香蕉放在一碗苹果中,因此,一碗苹果不是一碗水果。(并且通过类似的论点,一碗水果也不是一碗苹果。)由于您可以合法地对这两种类型执行的操作是不同的,所以它们是不兼容的。


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


在此处输入图片说明


所需的功能称为通用矛盾,当编译器可以证明差异是安全的,并且变化类型是引用类型时,只有接口和委托类型才支持该功能。例如,您可以IEnumerable<Apple>在IEnumerable<Fruit>需要的上下文中使用an ,因为编译器可以验证没有办法将a Banana放入一系列水果中。


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


查看完整回答
反对 回复 2019-11-19
?
开心每一天1111

TA贡献1836条经验 获得超13个赞

我接受了Eric的回答,因为它很好地解释了为什么无法实现我想要的东西,但是我还认为我会分享我的解决方案,以防其他人遇到同样的问题。


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


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


这是我的代码最终看起来如何的示例


// base classes

public abstract class BaseViewPresenter { }

public abstract class BaseView : UserControl 

{

    public BaseViewPresenter Presenter { get; set; }

}


public abstract class BaseView<T> : 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<LoginPresenter> 

{

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

}


查看完整回答
反对 回复 2019-11-19
  • 3 回答
  • 0 关注
  • 577 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信