Question

Is there a better way to mimic Covariance in this example? Ideally I'd like to do:

private IDictionary<string, ICollection<string>> foos;

public IEnumerable<KeyValuePair<string, IEnumerable<string>> Foos
{
    get
    {
        return foos;
    }
}

But KeyValuePair<TKey, TValue> is not covariant.

Instead I have to do:

public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Foos
{
    get
    {
        return foos.Select(x => 
            new KeyValuePair<string, IEnumerable<string>>(x.Key, x.Value));
    }
}

Is there a better/cleaner way?

Was it helpful?

Solution

Unfortunately, KeyValuePair<TKey, TValue> is a struct; and structs don't exhibit variance in .NET.

You can of course solve this by writing your own covariant Pair interface and some simple helpers to convert between sequences of KeyValuePair and your custom Pair interface. This will let you do:

var dict = new Dictionary<string, ICollection<string>>();

var view = dict.GetCovariantView(); // IEnumerable< IPair<string, ICollection<string> > >
  
// Notice that you can _widen_ both the key and the value types:
var dictView = view.CastPairs<object, IEnumerable<string>>(); // IEnumerable< IPair< object, IEnumerable<String> > >

// The `CastPairs` call is actually unnecessary provided you don't use `var` for the left-hand-side assignment.
// ...this is due to the implicit (and identity-preserving) variant interface conversion in C#, e.g.:
IEnumerable< IPair< Object, IEnumerable<String> > > dictView2 = view;

Console.WriteLine( Object.ReferenceEquals( view, dictView2 ) ); // --> True

Here's some example code that will let you achieve this:

// `out TKey` is for demonstration purposes. In production-quality code you probably should be using invariant key types.
public interface IPair<out TKey, out TValue>
    where TKey : notnull
{
    TKey   Key   { get; }
    TValue Value { get; }
}

public class Pair<TKey, TValue> : IPair<TKey, TValue>
    where TKey : notnull
{
    public TKey   Key   { get; }
    public TValue Value { get; }

    public Pair(TKey key, TValue value)
    {
        this.Key   = key;
        this.Value = value;
    }

    public Pair(KeyValuePair<TKey, TValue> pair)
        : this(pair.Key, pair.Value)
    {}
}

public static class PairSequenceExtensions
{
    public static IEnumerable<IPair<TKey, TValue>> GetCovariantView<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
        where TKey : notnull
    {
        if (source is null) throw new ArgumentNullException(nameof(source));

        return source.Select(kvp => new Pair<TKey, TValue>(kvp));
    }

    public static IEnumerable<IPair<TKey, TValue>> CastPairs<TKey, TValue>(this IEnumerable<IPair<TKey, TValue>> source)
        where TKey : notnull
    {
        if (source is null) throw new ArgumentNullException(nameof(source));

        return source;
    }
}

OTHER TIPS

Hardly. KVP is a struct: Not an itnerface, is of ValueType.

Interesting SO post on variance.

I think casts are more performant, so I'd prefer to code like this:

private IDictionary<string, IEnumerable<string>> foos;

public IEnumerable<KeyValuePair<string, IEnumerable<string>> Foos
{
    get
    {
        return foos;
    }
}

And cast KeyValuePair.Value to ICollection where I really need to. Frankly speaking it depends on how foos is used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top