Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KeyValuePair Covariance

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?

like image 587
Ben Foster Avatar asked Feb 16 '13 11:02

Ben Foster


People also ask

How to store key value in c#?

Hashtable represents a data structure that can store objects as key value pairs. You can search for a value in an instance of Hashtable class using the corresponding key. Note that both the key and the value that is stored in a Hashtable instance is of the object type. Note that the key cannot be null.

What is default for KeyValuePair?

default equals to null. And default(KeyValuePair<T,U>) is an actual KeyValuePair that contains null, null .

How to get key value pair from Dictionary in c#?

To add key-value pair in C# Dictionary, firstly declare a Dictionary. IDictionary<int, string> d = new Dictionary<int, string>(); Now, add elements with KeyValuePair.


1 Answers

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;
    }
}
like image 77
Ani Avatar answered Sep 24 '22 21:09

Ani