代码之家  ›  专栏  ›  技术社区  ›  Kasper van den Berg

如何将查找的远程信息,即i lookup<tkey,derived>转换为ilookup<tkey,base>?

  •  3
  • Kasper van den Berg  · 技术社区  · 6 年前

    我有一个 ILookup<int, Derived> 我想还一个 ILookup<int, Base> 哪里 Derived 实现或扩展 Base 是的。

    目前我使用 SelectMany() 然后 ToLookup() 首先提取 ILookup<int,派生> 住进公寓 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,远程通讯> 提出问题;但事实并非如此,所以我仍在继续寻找更好的方法。

    注意:我当然可以编写一个扩展方法来完成它,但这并不妨碍迭代和重新打包所需的计算工作。

    2 回复  |  直到 6 年前
        1
  •  3
  •   xanatos    6 年前

    可以创建代理:

    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 (这将不支持键的值类型,如您的示例中所示)。

        2
  •  1
  •   Paul Suart Wes    6 年前

    创建自己的类来实现 ILookup<TKey, TBase> ,就像这样:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApp2
    {
        class Program
        {
            static void Main(string[] args)
            {
                var lookup1 = new List<Derived> { new Derived() { Key = 1, Prop1 = "A", Prop2 = "B"} } .ToLookup(x => x.Key, x => x);
    
                var baseLookup = new BaseLookup<int, Base, Derived>(lookup1);
    
                Console.WriteLine(baseLookup[1]);
    
            }        
        }
    
        public class BaseLookup<TKey, TBase, TDerived> : ILookup<TKey, TBase> where TDerived : TBase
        {
            private readonly ILookup<TKey, TDerived> _inner;
    
            public BaseLookup(ILookup<TKey, TDerived> inner)
            {
                _inner = inner;
            }
    
            IEnumerator<IGrouping<TKey, TBase>> IEnumerable<IGrouping<TKey, TBase>>.GetEnumerator()
            {
                return (IEnumerator<IGrouping<TKey, TBase>>) _inner.GetEnumerator();
            }
    
            public IEnumerator GetEnumerator()
            {
                return ((IEnumerable) _inner).GetEnumerator();
            }
    
            public bool Contains(TKey key)
            {
                return _inner.Contains(key);
            }
    
            public int Count => _inner.Count;
    
            public IEnumerable<TBase> this[TKey key] => _inner[key].Cast<TBase>();
        }
    
        public class Base
        {
            public int Key { get; set; }
    
            public string Prop1 { get; set; }
        }
    
        public class Derived : Base
        {
            public string Prop2 { get; set; }
        }
    }