这是我的协变接口,其中一个返回和可枚举的方法也是协变的(一如既往)。简单的。
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
是协变有效的吗?我的意思是,它实际上是相同的关键字。这对我来说很有意义。