繁体   English   中英

类和具有泛型类型的接口的C#3.0隐式转换错误

[英]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下)。

IViewAIView派生的事实并不自动意味着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>这两种类型分配兼容。

这个问题实际上是关于泛型类型的协方差和矛盾。 我们有

IViewAIView

但这并不意味着

IPresenter<IViewA>IPresenter<IView>

结论成立时,我们说IPresenter<T>T协变的。 在C#中,通过在相关的类型参数(此处为T )之前放置out关键字,使接口协变,如下所示:

interface IPresenter<out T>  ...

由于您没有输入out关键字,因此不允许您执行任何操作。

但它是唯一安全的做一个类型协在T ,如果所有的用途T去“走出去”。 例如,可以将T用作方法的返回类型或用作get only属性的属性类型。

您的界面在“入”位置使用T ,即作为值参数(即,不带ref (或out )修饰符的参数)。 因此,使您的接口协变是非法的。 如果没有此限制,可能会发生的情况,请参见其他一些答案。

然后是矛盾的概念,对于IPresenter<>意味着

IViewAIView

暗示

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM