2

This is my covariant interface with one method returning and enumerable which is also covariant (as it always is). Simple.

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

Now I want to add more data to that enumerable by making use of a ValueTuple:

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

Now I get a compiler error stating that TData data must be invariantly valid. I see, ValueTuple is not covariant.


Alright, then I'll just roll my own very special tuple since I really want to be able to use that nice deconstruction syntax in the foreach later on:

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

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

But the compiler says no, still. This results in the same error, now on out TData data.


Alright compiler, you, what do you think about this one:

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

This compiles and works like a charm. But why? What is the difference between declaring the Deconstruct as an extension method rather than an interface method here? Where does covariance stop and invariance begin?

And moreover, shouldn't out parameters be covariantly valid? I mean, it's literally the same keyword. It would make sense to me.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Bruno Zell
  • 7,761
  • 5
  • 38
  • 46
  • 3
    https://stackoverflow.com/questions/2876315/ref-and-out-parameters-in-c-sharp-and-cannot-be-marked-as-variant – DavidG Dec 24 '18 at 15:00
  • @DavidG Interesting that in pure C# `out` parameters could be covariant, but the internals of the CLR and compatibility with other .Net languages make it impossible and theirfore forbidden. That solves the mystery about the `out` parameter at least. Thank you, sir. – Bruno Zell Dec 24 '18 at 15:09
  • @BrunoZell *Interesting that in pure C# out parameters could be covariant* [Answer](https://stackoverflow.com/a/2877515) to referenced question shows that `out` parameters could **not** be covariant in pure C# too. – user4003407 Dec 25 '18 at 03:46

0 Answers0