C#转换inheritance的通用接口
我遇到了一些麻烦,让我开始构建一个我想出的界面。 这是C#Windows Forms的MVP设计。 我有一个IView类,我在我的表单类上实现。 还有一个IPresenter,我将其导入各种特定的演示者。 每个Presenter将根据角色以不同方式管理IView,例如打开对话框以使用AddPresenter输入新数据集,而不是使用EditPresenter编辑现有数据,EditPresenter会将数据预加载到表单上。 每个都inheritance自IPresenter。 我想这样使用代码:
AddPresenter pres = new AddPresenter();
我基本上有这个工作,但这些演示者和他们管理的视图被捆绑到运行时加载的插件,这意味着我需要一个作为插件接口的Manager类采用“模式”参数。 此模式参数用于工厂方法来创建“添加”或“编辑演示者”,但由于稍后会调用显示对话框,因此我需要通过IPresenter接口进行调用,如下所示:
private IPresenter pres; public ShowTheForm() { pres.ShowDialog(); }
现在,我遇到的问题是将AddPresenter的具体实例表示为’pres’成员。 这是我所拥有的简化版本:
interface IView { void ViewBlah(); } interface IPresenter where V : IView { void PresBlah(); } class CView : IView { public void ViewBlah() { } } class CPresenter : IPresenter where T : IView { public void PresBlah() { } } private void button3_Click(object sender, EventArgs e) { CPresenter cpres = new CPresenter(); IPresenter ipres = (IPresenter)cpres; }
这是错误:
Unable to cast object of type 'CPresenter`1[MvpApp1.MainForm+CView]' to type 'IPresenter`1[MvpApp1.MainForm+IView]'.
我可以告诉Presenter和Generic类型规范是接口的子类,所以我无法理解为什么它不会转换。
有什么想法吗?
史蒂夫
问题是generics类型参数。 如果使接口参数协变,则转换将起作用。
这可以通过添加out
关键字来实现,如下所示:
interface IPresenter where V : IView { void PresBlah(); }
您可以通过以下MSDN文章了解有关其工作原理的更多信息: generics中的协方差和逆变 。 具有协变类型参数的通用接口部分特别适用于您的问题。
更新:确保检查@phoog和我之间的评论。 如果您的实际代码接受V
作为输入,您将无法使其协变。 引用的文章和@phoog的答案进一步详细解释了这个案例。
CPresenter
不是IPresenter
,就像List
不是IList
。
想一想。 如果你可以获得对List
的IList
引用,你可以向它添加一个string[]
,它必须抛出exception。 静态类型检查的重点是防止编译这样的代码。
如果接口允许,你可以将type参数声明为covariant( IPresenter
然后接口的行为将更像IEnumerable
。这只有在从未使用过type参数的情况下才有可能在输入位置。
要返回List
示例,可以将其视为IEnumerable
,因为您无法向IEnumerable
引用添加任何内容; 你只能读取它的内容,反过来,将int[]
视为IEnumerable
是安全的,所以一切都很好。