Domanda

I'm trying to write an extension method that will convert IDictionary<K, S<V>> holding any type of collection/sequence (S<V>) to ILookup<K, V> which is more proper data structure in those cases. This means I'd like my extension to work on different S types and interfaces:

  • IDictionary<K, IEnumerable<V>>
  • IDictionary<K, ICollection<V>>
  • IDictionary<K, List<V>>

etc. Ideally, I don't want to write separate implementation for each possible collection type AND I want type inference to do its job.

What I've tried is:

public static ILookup<TKey, TValue>ToLookup<TKey, TCollection, TValue>(
    this IDictionary<TKey, TCollection> dictionary)
        where TCollection : IEnumerable<TValue>

But it have no TValue in parameters list, so type inference is unable to figure it out - I get "The type arguments for method ToLookup cannot be inferred from the usage".

Is there a chance it could work somehow in other way than adding fake TValue-typed parameter to the method?

Examples of expected usage

I hope all above calls to be possible and result in a call to my single extension method:

var dictOfIEnumerables = new Dictionary<int, IEnumerable<int>>();
var lookupFromIEnumerables = dictOfIEnumerables.ToLookup();

var dictOfICollections = new Dictionary<int, ICollection<int>>();
var lookupFromICollections = dictOfICollections.ToLookup();

var dictOfLists = new Dictionary<int, List<int>>();
var lookupFromLists = dictOfLists.ToLookup();
È stato utile?

Soluzione

Because all collections implement IEnumerable<T>, we can just use it instead of the TCollection type paramter. Unfortunately the type inference does not know this. This is the code I wrote:

public static ILookup<TKey, TValue> ToLookup<TKey, TValue>
        (this IDictionary<TKey, IEnumerable<TValue>> dict)
{
    return dict.SelectMany(p => p.Value.Select
        (v => new KeyValuePair<TKey, TValue>(p.Key, v)))
        .ToLookup(p => p.Key, p => p.Value);
}

There seems to be no way of making the type inference work, but this method will work if you cast the Dictionary:

((IDictionary<int, IEnumerable<int>>)dictOfLists).ToLookup()

Also you can add Lists to a Dictionary of IEnumerables and cast them back when you need them.

Altri suggerimenti

From the bit of testing I've done, here are my results.

If I type dictOfIEnumerables.ToLookup(, I see 4 overloaded methods.
enter image description here

However, if I type dictOfIEnumerables.ToLookup<, I see all 5 overloaded methods. enter image description here

It appears that type inference isn't working, because of name collision/resolution collision between the ToLookup() that is defined on IEnumerable. Apparently, without the angle brackets, it's resolving to the methods defined on IEnumerable, because that is what TCollection is restricted to. Maybe someone on StackOverflow that is smarter than I can explain to you why it works the way it does.

However, using the specified types does, in fact, work correctly on my machine. enter image description here

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top