有没有办法将不同类型的数组传递给接口实现

Is there a way to pass arrays of different types to an interface implementation

所以我有以下场景:

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

void ParseFile(Array[] fileContents);

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

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

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

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

你目前的想法(使用Array不是Array[])和Byte[])是...ill-informed和 ill-advised.

首先,.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[].

解决方案:协变泛型!

这就是泛型类型(以及逆变和协变)的全部内容,以及为什么在设计多态接口时需要仔细考虑数据在您的内部移动的方向程序(一般来说,“forward”/“input”数据可以是“sh运行k”,“output”数据可以是“grown”/“expanded”/“extended”——但不是vice-versa。这就是 inout generic-type 修饰符的用途。

所以我会这样做:(免责声明:我不知道你的界面实际上是干什么用的,也不知道为什么你想要 Byte[] 之外的任何东西 ParseFile方法参数...)

interface IFileBuilder<in TInput>
{
    void ParseFile( IReadOnlyList<TInput> fileContents )
}

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

(请注意,在这种特殊情况下 contravariance/covariance 修饰符(in TInput 部分)的使用毫无意义,因为原语和 base-types 在 .NET 中(ByteInt32String 等)要么是没有继承的结构,要么是 sealed-types).

用法:

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>`.
    
}

所以你假设的 运行 时间错误现在是有保证的 compile-time 错误。

并且 compile-time 错误比 运行 时间错误要好得多 - 这种方法还意味着您可以在应用程序中推断 data-flow 的方向。