【问题标题】:Why is covariance not working as expected? [duplicate]为什么协方差不能按预期工作? [复制]
【发布时间】:2021-02-24 22:22:43
【问题描述】:

为什么这不起作用?编译器应该足够聪明,知道 InterfaceB 需要 InterfaceA,因此必须兼容。

public interface InterfaceA
{ }

public interface InterfaceB : InterfaceA
{ }

public abstract class DerivedClass : BaseClass
{
    protected override InterfaceB ItemService { get; set; } // Error, needs to be InterfaceA
}

public abstract class BaseClass
{
    protected virtual InterfaceA ItemService { get; set; }
}

这是为什么呢?

【问题讨论】:

  • 您不能更改合同
  • 这也没有意义......就像BaseClass derived = new DerivedClass();一样,你可以分配不是InterfaceB的东西,比如derived.ItemService= instanceOfImplementationOfInterfaceAAndNotInterfaceB;
  • 通过基类,您可以将任何接口A分配给可能未实现接口B的“ItemService”.....
  • 自 2009 年以来答案没有改变 - stackoverflow.com/a/1048929/477420... 正如@Selvin 指出的那样,问题中显示的示例没有意义 - 无法更改 get 和 set 的类型并支持差异(共同或反对)。您可能有理由期望它与 either get 或 set... 一起使用

标签: c# c#-9.0


【解决方案1】:

编译器应该足够聪明,知道 InterfaceB 需要 InterfaceA,因此必须兼容。

但它不兼容 - 你可以这样做:

DerivedClass derived = new DerivedClass();
InterfaceB ib = new InterfaceBImpl(); 
derived.ItemService = ib;              // good so far
InterfaceA ia = new InterfaceAImpl();  // still good
BaseClass bc = derived;                // still a legal downcast
bc.ItemService = ia;                   // seemingly good - BaseClass can store an InterfaceA
ib = derived.ServiceImpl;

这里是爆炸的地方。您已将未实现 InterfaceB 的对象存储在应该需要InterfaceB的属性中。

一种常见的方法仍然是泛型:

public abstract class DerivedClass : BaseClass<InterfaceB>
{
    //protected override InterfaceB ItemService { get; set; } // Error, needs to be InterfaceA
    // no override needed - ItemService will now be of type InterfaceB
}

public abstract class BaseClass<T> where T : InterfaceA
{
    protected T ItemService { get; set; }
}

我承认我还没有深入研究 C# 9,但据我所知,协变返回仅适用于 methodsget-only特性。如果是这种情况,那么您的解决方案就可以了。在这种情况下,正是 setter 允许您破坏类型系统。

似乎也支持 只读 属性,因此您也许可以这样做:


public abstract class DerivedClass : BaseClass
{
    protected override InterfaceB ItemService { get; } 
}

public abstract class BaseClass
{
    protected virtual InterfaceA ItemService { get; }
}

【讨论】:

  • “但从我所见,协变返回仅适用于方法。”不,两者都适用。返回类型协方差仅适用于仅获取属性或方法,这是有道理的。只有返回类型才允许协方差。二传手违反了这条规则。感谢您指出这一点。
【解决方案2】:

基类表示任何 InterfaceA 都可以分配给 ItemService,因此派生类不能将其更改为“只能将 InterfaceB 分配给它”。但是,如果您放弃分配,那就是:-

  public interface InterfaceB : InterfaceA
    {
    }

    public abstract class DerivedClass : BaseClass
    {
        protected override InterfaceB ItemService { get; } // no set
    }

    public abstract class BaseClass
    {
        protected virtual InterfaceA ItemService { get; }

    }

它会起作用,因为没有任务....但我不确定你的实际目标

【讨论】:

  • 是的,因为可以通过不同的方式设置变量,例如在派生类中覆盖 get only 属性仍然可以由 ctor 设置,我同意这一点。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-16
  • 2022-01-12
  • 1970-01-01
  • 2021-05-30
  • 2020-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多