我很难想出一个接口。这是针对C#Windows窗体的MVP设计。我有一个IView类,它是我在表单类上实现的。还有一个IPresenter,我将其衍生为各种特定的演示者。每个Presenter将根据角色的不同对IView进行不同的管理,例如,使用AddPresenter打开对话框以输入一组新数据,而不是使用EditPresenter编辑现有数据,EditPresenter会将数据预加载到表单上。这些都继承自IPresenter。我想这样使用代码:
AddPresenter<ConcreteView> pres = new AddPresenter<ConcreteView>();
我基本上已经完成了这项工作,但这些演示者和他们管理的视图被捆绑到插件中,这些插件在运行时后加载,这意味着我需要一个Manager类,它作为插件接口,采用"mode"参数。此模式参数用于创建Add或Edit Presenter的工厂方法,但由于稍后会调用显示对话框,因此我需要通过IPresenter接口进行调用,如下所示:
private IPresenter<IView> pres;
public ShowTheForm()
{
pres.ShowDialog();
}
现在,当涉及到AddPresenter的具体实例化时,我遇到了问题,比如说,对"pres"成员。以下是我所拥有的简化版本:
interface IView
{
void ViewBlah();
}
interface IPresenter<V> where V : IView
{
void PresBlah();
}
class CView : IView
{
public void ViewBlah()
{
}
}
class CPresenter<T> : IPresenter<T> where T : IView
{
public void PresBlah()
{
}
}
private void button3_Click(object sender, EventArgs e)
{
CPresenter<CView> cpres = new CPresenter<CView>();
IPresenter<IView> ipres = (IPresenter<IView>)cpres;
}
这就是错误:
Unable to cast object of type 'CPresenter`1[MvpApp1.MainForm+CView]' to type 'IPresenter`1[MvpApp1.MainForm+IView]'.
Presenter和Generic类型规范都来自我所能告诉的接口的ARE子类,所以我不明白为什么它不会强制转换。
有什么想法吗?
Steve
问题在于泛型类型参数。如果您使接口参数协变,那么强制转换将起作用。
这是通过添加out
关键字来实现的,比如:
interface IPresenter<out V> where V : IView
{
void PresBlah();
}
您可以通过以下MSDN文章了解更多关于这一点的信息:Generics中的协方差和方差。具有协变类型参数的通用接口部分专门适用于您的问题。
更新:确保你检查了@phoog和我之间的注释。如果你的实际代码接受V
作为输入,你将无法使其协变。引用的文章和@phoog的回答更详细地解释了这个案例
CPresenter<CView>
不是IPresenter<IView>
,就像List<int[]>
不是IList<IEnumerable>
一样。
想想看,如果你能得到一个对List<int>
的IList<IEnumerable>
引用,你就可以向它添加一个string[]
,这就必须抛出一个异常。静态类型检查的全部目的是防止编译此类代码。
如果接口允许,您可以将类型参数声明为协变(IPresenter<out V> where V : ...
)。然后接口的行为将更像IEnumerable<out T>
。只有当类型参数从未在输入位置使用时,这才有可能。
回到List<int[]>
的例子,将其视为IEnumerable<IEnumerable>
是安全的,因为不能向IEnumerable<T>
引用添加任何内容;你只能从中读出东西,反过来,把int[]
当作IEnumerable
是安全的,所以一切都很好。