2

这是我的协变接口,其中一个返回和可枚举的方法也是协变的(一如既往)。简单的。

public interface IDataSource<out TData> {
    IAsyncEnumerable<TData> StreamData(CancellationToken cancellationToken = default);
}

现在我想通过使用 a 向该可枚举添加更多数据ValueTuple

public interface IDataSource<out TData> {
    IAsyncEnumerable<(TData data, DateTime timestamp)>> StreamData(CancellationToken cancellationToken);
}

现在我收到一个编译器错误,说明它TData data必须始终有效。我明白了,ValueTuple不是协变的。


好吧,那么我将推出我自己的非常特殊的元组,因为我真的希望能够在foreach以后使用那种很好的解构语法:

public interface IDataSource<out TData> {
    IAsyncEnumerable<IDataTuple<TData>> StreamData(CancellationToken cancellationToken);
}

public interface IDataTuple<out TData> {
    void Deconstruct(out TData data, out DateTime timestamp);
}

但是编译器仍然说不。这会导致相同的错误,现在在out TData data.


好的编译器,你,你怎么看这个:

public interface IDataSource<out TData> {
    IAsyncEnumerable<IDataTuple<TData>> StreamData(CancellationToken cancellationToken = default);
}

public interface IDataTuple<out TData> {
    TData Data { get; }
    DateTime Timestamp { get; }
}

public static class DataTupleExtensions {
    public static void Deconstruct<TData>(this IDataTuple<TData> tuple, out TData data, out DateTime timestamp) {
        data = tuple.Data;
        timestamp = tuple.Timestamp;
    }
}

public async Task StreamData<TData>(IDataSource<TData> dataSource, CancellationToken cancellationToken = default) {
    await foreach (var (data, timestamp) in dataSource.StreamData(cancellationToken)) {
        // Do stuff
    }
}

这编译和工作就像一个魅力。但为什么?在这里声明为扩展方法而不是接口方法有什么区别?Deconstruct协方差在哪里停止,不变性从哪里开始?

而且,参数不应该out是协变有效的吗?我的意思是,它实际上是相同的关键字。这对我来说很有意义。

4

0 回答 0