[英]C# 3.0 implicit cast error with class and interfaces with generic type
我实现了一些依赖关系(这是MVP模式的一部分)。 现在,当我尝试执行转换时,VS会通知错误。
定义:
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下)。
IViewA
从IView
派生的事实并不自动意味着IPresenter<IViewA>
从IPresenter<IView>
派生。 实际上, IPresenter<IViewA>
和IPresenter<IView>
是两个不同的类型,它们之间没有继承关系。 他们唯一的共同祖先是object
。
让我们看一个例子。 假设我们有Animal
类,从Animal
派生的Cat
类和从Animal
派生的Dog
类。 现在,我们声明两个列表
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>
这两种类型分配兼容。
这个问题实际上是关于泛型类型的协方差和矛盾。 我们有
IViewA
是IView
但这并不意味着
IPresenter<IViewA>
是IPresenter<IView>
当结论成立时,我们说IPresenter<T>
在T
是协变的。 在C#中,通过在相关的类型参数(此处为T
)之前放置out
关键字,使接口协变,如下所示:
interface IPresenter<out T> ...
由于您没有输入out
关键字,因此不允许您执行任何操作。
但它是唯一安全的做一个类型协在T
,如果所有的用途T
去“走出去”。 例如,可以将T
用作方法的返回类型或用作get
only属性的属性类型。
您的界面在“入”位置使用T
,即作为值参数(即,不带ref
(或out
)修饰符的参数)。 因此,使您的接口协变是非法的。 如果没有此限制,可能会发生的情况,请参见其他一些答案。
然后是矛盾的概念,对于IPresenter<>
意味着
IViewA
是IView
暗示
IPresenter<IView>
是IPresenter<IViewA>
注意顺序如何随协方差变化。 仅当在“ in”位置使用类型参数(例如值参数)时,才可以安全使用协变。
根据唯一的成员,声明您的接口是不变的是合法的:
interface IPresenter<in T> ...
当然,这里in
意思是相反的。 但是它将颠倒允许隐式转换的“方向”。
通过将PresenterA
存储在IPresenter<IView>
,您的意思是“此对象具有接受任何IView
setView
方法”。
但是, PresenterA
的方法setView
仅接受IViewA
。 如果将其传递给IViewSomethingElse
,那么您期望发生什么?
它不起作用,因此编译器不允许。
您尝试执行的操作没有任何意义。 想象一下以下内容(包括一个有意义的方差事物):
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
的值是否恰好是“足够通用”的值,即使其编译时类型不是。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.