D 具体类数组与接口数组不协变

D 具体类数组与接口数组不协变,d,D,我在为我的dataaccess.mysqlclient模块提供一个抽象的基础层时遇到了一个小问题,在这个模块中,我定义了一组接口来满足最低要求,并定义了一组实现这些要求的类 现在,dmd编译器抱怨: 错误:@property MySqlColumnInfo[]()类型的函数dataaccess.mysqlclient.MySqlReader.columns覆盖但不与@property IDbColumnInfo[]类型的dataaccess.dbclient.idbdreader.columns

我在为我的
dataaccess.mysqlclient
模块提供一个抽象的基础层时遇到了一个小问题,在这个模块中,我定义了一组接口来满足最低要求,并定义了一组实现这些要求的类

现在,dmd编译器抱怨:

错误:@property MySqlColumnInfo[]()类型的函数dataaccess.mysqlclient.MySqlReader.columns覆盖但不与@property IDbColumnInfo[]类型的dataaccess.dbclient.idbdreader.columns共变()
退出代码1

相关代码行如下所示:

IDB阅读器:

interface IDbReader
{
    @property IDbColumnInfo[] columns();
    // ... 
}
MySqlReader:

class MySqlReader : IDbReader
{
    private MySqlColumnInfo[] _columns;
    @property public MySqlColumnInfo[] columns() {return _columns;}
    // ... 
}
有几种方法可以解决这个编译器问题

  • 将具体属性声明为
    IDbColumnInfo[]
  • 将数组包装在列表类中
如果我想得久一点的话,可能还会有一些。但这些看起来都不太优雅

大问题来了:

  • 我是否忽略了一些简单的事情
  • 实现的数组可以与接口的数组协变吗

此外,我真的无法想象编译器抱怨的原因。我的代码中有更复杂的结构,它们编译得很好。因此,如果有人能解释为什么这不能按原样工作,我们将不胜感激。

您遇到了这样的问题

让我们假设您的代码编译得很好。现在,考虑下面的代码:

class SomeOtherColumnInfo : IDbColumnInfo {}

IDbReader reader = new MySqlReader(...);
IDbColumnInfo[] columns = reader.columns;
columns[3] = new SomeOtherColumnInfo(); // OK
由于数组是可变的,我们可以用其他
IDbColumnInfo
派生类的实例覆盖它的元素。问题是我们还修改了
MySqlReader
的私有
\u列
字段。因此,现在我们有一个
SomeOtherColumnInfo
实例作为
MySqlColumnInfo[]
数组的成员。因此,我们在不使用强制转换或其他不安全代码的情况下破坏了类型系统。由于编译器会意外地阻止我们这样做,因此它将拒绝隐式地将可变类数组转换为其他类的数组,即使这些类是相关的


现在,我认为如果返回的数组不可变(即const或immutable),则D允许编译是有意义的。然而,编译器也不喜欢这样。我不知道这是否是遗漏,或者是否有我不知道的原因。

我也这么想,但不明白为什么会造成问题。因为,据我所知,
SomeOtherColumnInfo
在本例中必须是
MySqlColumnInfo
的子类。换言之;我不明白这是如何破坏类型系统的。@克里斯想一想,如果您将
MySqlReader[]
转换为
idbdreader[]
,然后在其上附加一个
PosixReader
,或者将其中一个元素替换为
PosixReader
,会发生什么。您只需将一个
PosixReader
放入真正的
MySqlReader[]
数组中,这肯定会破坏类型系统。一般来说,将
DerivedClass
的容器转换为
BaseClass
的容器是没有意义的,就像一开始看起来的那样。@JonathanMDavis:是的,我有点明白,但我不能真正理解他们都实现了相同的契约,所以应该放在相同的位置。如果它们都不是
Duck
,程序员就不会那样标记它们了,对吗?只要类型系统在该场景中断言
PosixReader
MySqlReader
的子类。
List
不是c#中
List
的一个子类吗?(我相信是的,但还没有检查)问题似乎可以通过使用
MySqlColumnInfoList:ArrayWrapper来解决!(IDbColumnInfo)
这里。现在,我还需要在一些情况下这样做。引用MSDN:“由于数组协方差,对引用类型数组元素的赋值包括运行时检查,以确保分配给数组元素的值实际上是允许的类型。”这是在C#中允许List成为List的子类的唯一原因。需要认识到的重要一点是,它们不实现相同的约定,因为数组有两个操作-get元素,它实现协方差(因为它们返回一个超类型),set元素(或append元素),它,因为它将新元素作为参数,所以需要逆变。因此,无论您以何种方式施放阵列,都必须放弃其一半契约。因此,可以对称地将对象[]之类的东西投射到Apple[*],其中T[*]是一个“盲数组”或“逆变数组”,它只支持赋值和追加,而不支持查找。