我有一个ILookup<int, Derived>
,我想返回一个ILookup<int, Base>
,其中Derived
实现或扩展Base
。
当前,我先使用SelectMany()
,然后使用ToLookup()
将ILookup<int, Derived>
的键值对提取到平面的IEnumerable
中,然后创建一个新的ILookup<int, Base>
:
class Base { }
class Derived: Base { }
class Test
{
ILookup<int, Base> CastLookup(ILookup<int, Derived> existing)
{
IEnumerable<KeyValuePair<int, Base>> flattened = existing.SelectMany(
(x) => x,
(gr, v) => new KeyValuePair<int, Base>(gr.Key, (Base)v)); // I know the explicit cast can be implicit here; it is just to demonstrate where the up casting is happening.
ILookup<int, Base> result = flattened.ToLookup(
(x) => x.Key,
(x) => x.Value);
return result;
}
}
如何在不迭代其条目然后重新打包的情况下转换ILookup?
注意:一个相关的问题是Shouldn't ILookup<TKey, TElement> be (declared) covariant in TElement?的bigge。 Ryszard Dżegan answers主要是由于历史原因:
ILookup<TKey, TElement>
是在具有协方差的泛型之前开发的。Herzmeister向something similar询问
Dictionary<TKey, TValue>
。 Mehrdad Afshari answers对于可变字典,协方差将是不安全的。确实,如果
Ilookup<TKey, TElement>
在TElement
中是协变的,那么我就不会遇到ILookup<TKey, TElement>
强制转换问题的这种情况;但事实并非如此,因此我对更好方法的追求仍在继续。注意:我当然可以编写一个扩展方法来做到这一点,但这并不能防止迭代和重新打包所需的计算工作。
最佳答案
您可以创建一个代理:
public static ILookup<TKey, TValueBase> ToLookupBase<TKey, TValue, TValueBase>(this ILookup<TKey, TValue> lookup)
where TValue : class, TValueBase
{
return new LookupProxy<TKey, TValue, TValueBase>(lookup);
}
public class LookupProxy<TKey, TValue, TValueBase> : ILookup<TKey, TValueBase>
where TValue : class, TValueBase
{
private readonly ILookup<TKey, TValue> lookup;
public LookupProxy(ILookup<TKey, TValue> lookup)
{
this.lookup = lookup;
}
public IEnumerable<TValueBase> this[TKey key] => lookup[key];
public int Count => lookup.Count;
public bool Contains(TKey key) => lookup.Contains(key);
public IEnumerator<IGrouping<TKey, TValueBase>> GetEnumerator() => lookup.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
请注意,您必须:
var base = existing.ToLookupBase<int, Derived, Base>();
因此明确地告诉所有通用参数。如果您甚至想支持
TKey
的协方差,它会稍微复杂一点,并且需要单独的支持类和单独的方法:public static ILookup<TKeyBase, TValueBase> ToLookupBase2<TKey, TValue, TKeyBase, TValueBase>(ILookup<TKey, TValue> lookup)
where TKey : class, TKeyBase
where TValue : class, TValueBase
{
return new LookupProxy2<TKey, TValue, TKeyBase, TValueBase>(lookup);
}
public class LookupProxy2<TKey, TValue, TKeyBase, TValueBase> : ILookup<TKeyBase, TValueBase>
where TKey : class, TKeyBase
where TValue : class, TValueBase
{
private readonly ILookup<TKey, TValue> lookup;
public LookupProxy2(ILookup<TKey, TValue> lookup)
{
this.lookup = lookup;
}
public IEnumerable<TValueBase> this[TKeyBase key] => key is TKey ? lookup[(TKey)key] : Enumerable.Empty<TValueBase>();
public int Count => lookup.Count;
public bool Contains(TKeyBase key) => key is TKey ? lookup.Contains((TKey)key) : false;
public IEnumerator<IGrouping<TKeyBase, TValueBase>> GetEnumerator() => lookup.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
这是因为您需要添加
where TKey : class, TKeyBase
(不支持该键的值类型,例如您的示例)。