2013-02-08 65 views
1

我实现了一些依赖项(它们是MVP模式的一部分)。现在,当我尝试执行投射时,VS会通知有关错误。C#3.0隐含类和具有泛型类型的接口的转换错误

定义:

interface IView 
{ 
    void setPresenter(IPresenter<IView> presenter); 
} 

interface IViewA : IView 
{ 
} 

interface IPresenter<T> where T : IView 
{ 
    void setView(T view); 
} 

class PresenterA : IPresenter<IViewA> 
{ 
} 

隐式转换:

IPresenter<IView> presenter = new PresenterA(); 

编译错误: 无法隐式转换类型 'PresenterA' 到 'IPresenter'。一个显式转换存在(是否缺少强制转换?)

明确的转换:

IPresenter<IView> presenter = (IPresenter<IView>)new PresenterA(); 

运行时错误:InvalidCastException的

我怎样才能解决这个问题,以保持这一概念?泛型类型的概念(我的前一个没有它)。我已经尝试过其他帖子中提到的变异和逆变问题(进出),但也有错误(根据VS 2010)。

+0

你不能,约束是没有意义的。 – millimoose 2013-02-08 18:58:28

+0

哪个约束? IPresenter中包含的通用类型? – Bronek 2013-02-08 19:02:57

+0

我去了它的细节,在[我的回答]例子(http://stackoverflow.com/a/14779661/41655)。 – millimoose 2013-02-08 19:10:41

回答

4

IViewAIView派生的事实并不表示IPresenter<IViewA>派生自IPresenter<IView>。实际上IPresenter<IViewA>IPresenter<IView>是两个不同的类型,它们之间没有继承关系。他们唯一的共同祖先是object

让我们来看一个例子。假定我们有一个Animal类别,类别来自Animal,而Dog类别来自Animal。现在让我们声明两个列表

List<Animal> animals; 
List<Cat> cats = new List<Cat>(); 

而且,我们还假设下面的任务是可能的:

animals = cats; 
animals.Add(new Cat()); // OK 
animals.Add(new Dog()); // Ooops! 

这份名单是在现实中的猫名单,我们正在尝试添加一只狗!因此List<Animal>List<Cat>这两种类型不允许分配兼容。

1

通过将PresenterA存储在IPresenter<IView>中,表示“此对象的方法setView”接受任何IView“。

但是,PresenterA的方法setView只接受IViewA。如果你通过它IViewSomethingElse,你会期望发生?

它不起作用,所以编译器不允许它。

0

你想要做的事情没有意义。想象一下以下(包括一个变化的thingie这是有道理的):现在

interface IView {} 

interface IViewA : IView {} 
class ViewA : IViewA {} 

interface IViewB : IView {} 
class ViewB : IViewB {} 

interface IPresenter<in T> where T : IView 
{ 
    void setView(T view); 
} 

class PresenterA : IPresenter<IViewA> 
{ 
    public void setView(IViewA view) {} 
} 

class PresenterB : IPresenter<IViewB> 
{ 
    public void setView(IViewA view) {} 
} 

,如果转换你正在试图做的是有效的,你可以这样做:

IPresenter<IView> presenter = new PresenterA(); 
presenter.setView(new ViewB()); 

,你可以看,这不是类型安全的。即你相信这些类型之间的关系不存在。

让什么变化你做的是相反的:

class Presenter : IPresenter<IView> 
{ 
    public void setView(IView view) {} 
} 

IPresenter<IViewA> presenter = new Presenter(); 

Presenter.setView()可以接受任何IView参数,因此可以接受IViewB。这也是编译器提到显式转换的原因。这是为了让你做到以下几点:

IPresenter<IViewA> presenterA = new Presenter(); 
IPresenter<IView> presenter = (IPresenter<IView>) presenterA; 

也就是说,要检查,如果你在运行时分配给presenter值恰好是这是“通用不够”,即使其编译时类型ISN”吨。

2

这个问题实际上是关于泛型类型的协变和逆变。我们有

an IViewA is an IView

但这并不自动意味着

an IPresenter<IViewA> is an IPresenter<IView>

的结论成立,我们说IPresenter<T>T。在C#中一个通过把一个out关键字有问题的类型参数(这里T)之前,为使接口协:

interface IPresenter<out T> ... 

既然你没有把out关键字,你做了什么是不允许的。

但是,如果T的所有用途都“熄灭”,则仅在T中进行类型协变才是安全的。例如,可以使用T作为方法的返回类型,也可以将其作为get的属性类型 - 仅属性。

您的接口在“in”位置使用T,即作为值参数(即没有0​​(或out)修饰符的参数)。因此,使你的界面协变是非法的。查看一些其他答案,了解如果此限制不存在会发生什么情况的示例。

然后还有的逆变的概念,这对于IPresenter<>意味着

an IViewA is an IView

意味着

an IPresenter<IView> is an IPresenter<IViewA>

注意如何与逆变顺序变化。当在“in”位置使用类型参数(例如值参数)时,逆变仅是安全的(并且只允许)。

基于唯一的成员,它是legat来声明接口逆变:

interface IPresenter<in T> ... 

其中in意味着逆变,当然。但它会扭转允许隐式转换的“方向”。