我遇到了一些麻烦,让我开始构建一个我想出的界面。 这是C#Windows Forms的MVP设计。我有一个IView类,我在我的表单类上实现。还有一个IPresenter,我将其导入各种特定的演示者。每个Presenter将根据角色以不同方式管理IView,例如打开对话框以使用AddPresenter输入新数据集,而不是使用EditPresenter编辑现有数据,EditPresenter会将数据预加载到表单上。其中每个都继承自IPresenter。我想这样使用代码:
AddPresenter<ConcreteView> pres = new AddPresenter<ConcreteView>();
我基本上有这个工作但是这些演示者和他们管理的视图被捆绑到运行时加载的插件中,这意味着我需要一个作为插件接口的Manager类采用“模式”参数。此模式参数用于工厂方法来创建“添加”或“编辑演示者”,但由于稍后会调用显示对话框,因此我需要通过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类型规范是接口的子类,所以我无法理解为什么它不会被强制转换。
有什么想法吗?
史蒂夫
答案 0 :(得分:24)
问题是泛型类型参数。如果使接口参数变为协变,那么转换将起作用。
这是通过添加out
关键字来完成的,如下所示:
interface IPresenter<out V> where V : IView
{
void PresBlah();
}
您可以使用以下MSDN文章了解有关其工作原理的更多信息:Covariance and Contravariance in Generics。 Generic Interfaces with Covariant Type Parameters部分特别适用于您的问题。
更新:确保检查@phoog和我之间的评论。如果您的实际代码接受V
作为输入,您将无法使其协变。引用的文章和@phoog的答案更详细地解释了这个案例。
答案 1 :(得分:8)
CPresenter<CView>
不是IPresenter<IView>
,就像List<int[]>
不是IList<IEnumerable>
一样。
想一想。如果您可以获得对IList<IEnumerable>
的{{1}}引用,则可以向其添加List<int>
,这必须抛出异常。静态类型检查的重点是防止编译这样的代码。
如果接口允许,你可以将type参数声明为covariant(string[]
。然后接口的行为更像IPresenter<out V> where V : ...
。这只有在输入中从不使用type参数时才有可能位置。
要返回IEnumerable<out T>
示例, 可以安全地将其视为List<int[]>
,因为您无法向{{1}添加任何内容参考;你只能阅读它中的内容,反过来,将IEnumerable<IEnumerable>
视为IEnumerable<T>
是安全的,所以一切都很好。