IDictionary上的单一扩展方法<k,ienumerable =“”ilist =“”icollection <v =“”>&gt; </k,>

时间:2013-06-26 20:48:00

标签: c# generics extension-methods covariance generic-variance

我正在尝试编写一个扩展方法,将IDictionary<K, S<V>>保存任何类型的集合/序列(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>

但它在参数列表中没有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();

2 个答案:

答案 0 :(得分: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);
}

似乎无法使类型推断工作,但是如果你强制转换词典,这个方法将起作用:

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

此外,您可以将列表添加到IEnumerables的词典中,并在需要时将其强制转换。

答案 1 :(得分:0)

从我已经完成的测试中,我的结果如下。

如果我输入dictOfIEnumerables.ToLookup(,我会看到4种重载方法 enter image description here

但是,如果我输入dictOfIEnumerables.ToLookup<,我会看到所有5个重载方法。 enter image description here

由于在IEnumerable上定义的ToLookup()之间的名称冲突/解决冲突,似乎类型推断不起作用。显然,没有尖括号,它就会解析IEnumerable上定义的方法,因为这就是TCollection的限制。也许StackOverflow上的某个人比我更聪明,可以向你解释它为什么会这样运作。

但是,使用指定的类型确实可以在我的机器上正常工作。 enter image description here