提问者:小点点

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


我有以下课程

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

激活器。CreateInstance(viewType)正确解析为myLoginView的新实例,但是控件作为BaseView

是否有一种方法可以正确地将控件转换为BaseView

因为LoginView继承自BaseView

我坚持使用。净3.5


共3个答案

匿名用户

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

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

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

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

您需要的特性称为泛型逆变,只有在编译器能够证明方差是安全的,并且变量类型是引用类型时,接口和委托类型才支持该特性。例如,可以使用IEnumerable

在本网站或web上搜索“C#convariance and contravariance”,您将发现有关此功能工作原理的更多详细信息。特别是,我关于如何在C#4中设计和实现此功能的系列文章从这里开始:http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx

匿名用户

我接受了埃里克的回答,因为它很好地解释了为什么我想要的是不可能的,但我也想分享我的解决方案,以防其他人遇到同样的问题。

我从原始的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;
}

匿名用户

您希望将该类型视为泛型参数的协变类型。类永远不可能是协变的;您需要使用一个接口而不是(或附加)一个抽象类,以使它相对于T协变。您还需要使用C#4.0。