有没有一种方法可以将不同类型的数组传递给接口实现



所以我有以下场景:

我正在编写一个程序,它接受各种类型(字符串、字节等(数组中的内容,将内容解析为数据结构,然后将结构化数据存储在某个地方。由于解析方法可能会有很大的不同,这取决于接收到的数据类型,所以我想定义单独的文件解析类,但要定义它们都应该实现的方法和属性。我想使用一个接口来实现这一点,但我不知道如何告诉接口,例如,它的ParseFile方法将接受某种类型的数组作为输入参数。如果我在一个界面中写下如下内容:

void ParseFile(Array[] fileContents);

然后尝试在ByteParser类中实现该方法,如下所示:

public void ParseFile(Byte[] fileContents){
//Do whatever
}

我得到一个编译器错误,我的实现类没有实现ParseFile方法。我认为这是因为编译器默认情况下不进行强制转换,即使ByteArray是Array的派生,它也不是Array类型。

有没有什么方法可以做我在这里试图做的事情,强制接口接受任何类型的数组,并允许我的实现类从那里处理它?

您当前的想法(使用Array(而不是Array[](和Byte[](……信息不灵通,建议不周。

例如。NET要求接口的实现与接口定义完全匹配,因此如果您有方法void ParseFile(Array fileContents),则实现必须也有void ParseFile(Array fileContents),而不能有ParseFile(Byte[] fileContents)。以下是您不能的原因:

假设这实际上是编译的:

interface IFileBuilder {
void ParseFile(Array fileContents);
}
class OctetFileBuilder : IFileBuilder {
public void ParseFile(Byte[] fileContents) {
// ...
}
}

然后运行这个:

void Main() {

Byte[]   byteArray = new Byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
String[] strArray  = new String[] { "foo", "bar", "baz" };
OctetFileBuilder ofb = new OctetFileBuilder();
ofb.ParseFile( byteArray ); // OK, so far so good.
ofb.ParseFile( strArray ); // Compiler rejects this because `String[]` isn't `Byte[]`. That's also OK.
IFileBuilder fb = ofb;
fb.ParseFile( byteArray ); // This would be okay because a `Byte[]` can be the argument for an `Array` parameter.
fb.ParseFile( strArray ); // But what happens here?

}

最后一行是问题:fb.ParseFile( strArray )

实现需要Byte[],但它被传递了一个String[]

我想你会期待的。NET引发运行时错误,或者它会神奇地知道如何将String[]转换为Byte[](不,不会(。相反,整个程序根本无法编译。

现在,在现实中,为了构建,您的实现必须是这样的

class OctetFileBuilder : IFileBuilder {
public void ParseFile(Array fileContents) {
Byte[] byteArray = (Byte[])fileContents; // Runtime cast.
}
}

因此,现在您的代码将编译并开始运行,但它在完成之前就会崩溃,因为当ParseFile被赋予String[]时,它将导致InvalidCastException,因为您无法有意义地直接从String[]强制转换为Byte[]

解决方案:协变泛型

这就是泛型类型(以及逆变和协方差(的全部内容,也是为什么在设计多态接口时,您需要仔细考虑程序内数据移动的方向(一般来说,"正向"/"输入"数据可以"收缩","输出"数据可以是"增长"/"扩展"/"延伸"-但不能反过来。这就是inout通用类型修饰符的作用

所以我会这样做:(免责声明:我不知道你的接口实际上是用来做什么的,也不知道为什么你想要ParseFile方法参数使用Byte[]以外的任何东西…(

interface IFileBuilder<in TInput>
{
void ParseFile( IReadOnlyList<TInput> fileContents )
}
class OctetFileBuilder : IFileBuilder<Byte> {
public void ParseFile(Byte[] fileContents) {
// ...
}
}

(请注意,在这种特殊情况下使用反方差/协方差修饰符(in TInput部分(是毫无意义的,因为中的基元和基类型。NET(ByteInt32String等(要么是没有继承的结构,要么是密封类型(。

用法:

void Main() {

Byte[]   byteArray = new Byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
String[] strArray  = new String[] { "foo", "bar", "baz" };
OctetFileBuilder ofb = new OctetFileBuilder();
ofb.ParseFile( byteArray ); // OK, so far so good.
ofb.ParseFile( strArray ); // Compiler rejects this because `String[]` isn't `Byte[]`. That's also OK.
IFileBuilder<Byte> fb = ofb;
fb.ParseFile( byteArray ); // OK
fb.ParseFile( strArray ); // Compiler error: `String[]` is not `IReadOnlyList<Byte>`.

}

因此,假设的运行时错误现在是一个有保证的编译时错误。

编译时错误比运行时错误要好得多,这种方法也意味着您可以推断应用程序中数据流的方向。

最新更新