1

我实现了一些依赖项(这是 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 下)。

4

4 回答 4

4

IViewA源自的事实IView并不自动意味着IPresenter<IViewA>源自IPresenter<IView>。实际上IPresenter<IViewA>IPresenter<IView>是两种不同的类型,它们之间没有继承关系。他们唯一的共同祖先是object

让我们看一个例子。假设我们有一个类Animal,一个Cat派生自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>不允许赋值兼容。

于 2013-02-08T19:00:06.750 回答
2

This question is really about covariance and contravariance of generic types. We have

an IViewA is an IView

but that does not automatically mean that

an IPresenter<IViewA> is an IPresenter<IView>

When the conclusion holds, we say that IPresenter<T> is covariant in T. In C# one makes an interface covariant by putting an out keyword before the type parameter in question (here T), as in:

interface IPresenter<out T>  ...

Since you didn't put the out keyword, what you do is not allowed.

But it is only safe to make a type covariant in T if all the uses of T goes "out". For example it is okay to use T as return type of methods or as property type for get-only properties.

Your interface uses T in an "in" position, namely as a value parameter (i.e. a parameter without the ref (or out) modifier). So it would be illegal to make your interface covariant. See some of the other answers for examples of what could happen if this restriction wasn't there.

Then there's the notion of contravariance, which for IPresenter<> means that

an IViewA is an IView

implies that

an IPresenter<IView> is an IPresenter<IViewA>

Notice how the order changes with contravariance. Contravariance is only safe (and only allowed) when the type parameter is used in "in" positions, such as value parameters.

Based on the only member, it would be legat to declare your interface contravariant:

interface IPresenter<in T>  ...

where in means contravariant, of course. But it will reverse the "direction" in which implicit conversions are allowed.

于 2013-02-08T19:51:01.420 回答
1

通过将 a 存储PresenterA在 an 中IPresenter<IView>,您是在说“这个对象有一个setView可以接受任何方法的方法IView”。

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

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

于 2013-02-08T19:02:09.600 回答
0

你试图做的事情没有意义。想象一下以下情况(包括一个有意义的变量):

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在运行时分配的值是否恰好是“足够通用”的值,即使它的编译时类型不是。

于 2013-02-08T19:09:10.913 回答