IDictionary <K,IEnumerable/IList/ICollection <V >>上的单一扩展方法

NOt*_*Dev 5 c# generics extension-methods covariance generic-variance

我正在尝试编写一个扩展方法,该方法将转换IDictionary<K, S<V>>保存任何类型的collection/sequence(S<V>),ILookup<K, V>在这些情况下,这是更合适的数据结构.这意味着我希望我的扩展能够在不同的S类型和接口上工作:

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

理想情况下,我不想为每种可能的集合类型编写单独的实现,并且我希望类型推断能够完成它的工作.

我试过的是:

public static ILookup<TKey, TValue>ToLookup<TKey, TCollection, TValue>(
    this IDictionary<TKey, TCollection> dictionary)
        where TCollection : IEnumerable<TValue>
Run Code Online (Sandbox Code Playgroud)

但它没有TValue参数列表,所以类型推断无法弄明白 - 我得到"方法ToLookup的类型参数不能从用法推断".

是否有可能以某种方式以某种方式工作,而不是在方法中添加假的TValue参数?

预期用途的示例

我希望所有上述调用都成为可能,并导致调用我的单个扩展方法:

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();
Run Code Online (Sandbox Code Playgroud)

tim*_*edt 1

因为所有集合都实现了IEnumerable<T>,所以我们可以使用它来代替TCollection类型参数。不幸的是,类型推断不知道这一点。这是我写的代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

似乎没有办法使类型推断起作用,但是如果您强制转换字典,则此方法将起作用:

((IDictionary<int, IEnumerable<int>>)dictOfLists).ToLookup()
Run Code Online (Sandbox Code Playgroud)

您还可以将列表添加到 IEnumerables 字典中,并在需要时将它们转换回来。