我有一个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>
强制转换问题的这种情况;但事实并非如此,所以我对更好方法的追求仍然在继续。
注意:我当然可以编写一个扩展方法来做到这一点,但这不会阻止迭代和重新打包所需的计算工作。
答案 0 :(得分:3)
您可以创建代理:
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
(就像您的示例一样,该键不支持键的值类型)。
答案 1 :(得分:1)
如何创建自己的实现console.log(PizzaSize.MEDIUM); // prints 'MEDIUM'
的类,如下所示:
ILookup<TKey, TBase>