我有以下课程
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
这是一个非常常见的问题。让我们重命名您的类型:
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。