【问题标题】:Initializing Lookup<int, string>初始化查找<int, string>
【发布时间】:2011-01-22 23:00:55
【问题描述】:

如何在 c# 的对象初始化例程中为属性声明一个新的查找类?

例如

new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> } 

类别位总是出现编译错误。


我有一个名为 Category 的属性,它的类型为 Lookup&lt;int, string&gt;,我想通过

实例化这个属性
new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> };

但我无法克服编译错误。

【问题讨论】:

  • 我无法真正理解您在这里要做什么...也许添加更多信息?
  • 很遗憾 Lookups 没有构造函数。我比 Dictionary 更喜欢它们,因为 Key 属性派生自值对象。比字典要干得多,因为每次添加项目时,Person.SSN 都是关键。
  • @AaronLS 我猜你会爱上KeyedCollection! :) 您所要做的就是继承它,并实现告诉它如何从值中检索键的方法。 (它是一种将值作为输入并从中返回键的方法。)......但是请注意,它确实有一些陷阱和怪癖 - 我建议阅读它并做在认真使用之前进行一些测试。

标签: c# linq class lookup


【解决方案1】:

根据 MSDN 文档,Lookup 类没有公共构造函数:http://msdn.microsoft.com/en-us/library/bb460184.aspx

您可以通过在实现IEnumerable&lt;T&gt; 的对象上调用ToLookup 来创建Lookup&lt;TKey, TElement&gt; 的实例。

你会想要做这样的事情:

new Component { ID = 1, Name = "MOBO", Category = new[] { … }.ToLookup(…) }

更新地址 cmets:

我不确定你是从哪里得到你的类别信息的,所以我会弥补一些……

new Component {
    ID = 1, 
    Name = "MOBO", 
    Category = new Dictionary<int, string> { 
        { 3, "Beverages" }
        { 5, "Produce" }
    }.ToLookup(o => o.Key, o => o.Value)
}

我的猜测是您的类别将来自其他来源,而不是像我在这里那样实例化字典。

【讨论】:

  • 我试过这个但没有得到智能感知,应该是什么在 ... 部分?
【解决方案2】:

来自MSDN

没有公共构造函数来创建Lookup&lt;TKey, TElement&gt; 的新实例。
此外,Lookup&lt;TKey, TElement&gt; 对象是不可变的,也就是说,您不能在 Lookup&lt;TKey, TElement&gt; 对象创建后添加或删除元素或键。

【讨论】:

    【解决方案3】:

    这是我对此的尝试。确保密钥是不可变的 (Gist)。

    public class MultiValueDictionary<TKey, TElement>
    : Collection<TElement>, ILookup<TKey, TElement>
    {
      public MultiValueDictionary(Func<TElement, TKey> keyForItem)
        : base(new Collection(keyForItem))
      {
      }
    
      new Collection Items => (Collection)base.Items;
    
      public IEnumerable<TElement> this[TKey key] => Items[key];
      public bool Contains(TKey key) => Items.Contains(key);
      IEnumerator<IGrouping<TKey, TElement>>
        IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator() => Items.GetEnumerator();
    
      class Collection
      : KeyedCollection<TKey, Grouping>, IEnumerable<TElement>, IList<TElement>
      {
        Func<TElement, TKey> KeyForItem { get; }
    
        public Collection(Func<TElement, TKey> keyForItem) => KeyForItem = keyForItem;
        protected override TKey GetKeyForItem(Grouping item) => item.Key;
    
        public void Add(TElement item)
        {
          var key = KeyForItem(item);
          if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
            collection.Add(item);
          else
            Add(new Grouping(key) { item });
        }
    
        public bool Remove(TElement item)
        {
          var key = KeyForItem(item);
          if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)
            && collection.Remove(item))
          {
            if (collection.Count == 0)
              Remove(key);
            return true;
          }
          return false;
        }
    
        IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
        {
          foreach (var group in base.Items)
            foreach (var item in group)
              yield return item;
        }
    
        const string IndexError = "Indexing not supported.";
        public int IndexOf(TElement item) => throw new NotSupportedException(IndexError);
        public void Insert(int index, TElement item) => Add(item);
        public bool Contains(TElement item) => Items.Contains(item);
        public void CopyTo(TElement[] array, int arrayIndex) =>
        throw new NotSupportedException(IndexError);
        new IEnumerable<TElement> Items => this;
        public bool IsReadOnly => false;
        TElement IList<TElement>.this[int index]
        {
          get => throw new NotSupportedException(IndexError);
          set => throw new NotSupportedException(IndexError);
        }
      }
    
      class Grouping : Collection<TElement>, IGrouping<TKey, TElement>
      {
        public Grouping(TKey key) => Key = key;
        public TKey Key { get; }
      }
    }
    

    【讨论】:

    • 如何使用它? keyForIte 仅用于字典?没有字典怎么用?
    【解决方案4】:

    如果您出于某种原因只需要返回一个空的ILookup,您可以从空字典中返回一个。例如,要创建一个ILookup&lt;string, int&gt;,您可以使用:

    return new Dictionary<string, int>().ToLookup(kvp => kvp.Key, kvp => kvp.Value);
    

    不幸的是,这是我能看到的最简洁的方法,无需自己创建一个实现ILookup 的类。

    【讨论】:

      【解决方案5】:

      您不能只使用 ToLookup;你必须告诉它如何找到键和值:

      // from ChaosPandion's code
      using System.Linq; // make sure you have the using statement 
      
      var component = new Component()  
      {  
          ID = 1,  
          Name = "MOBO",  
          Category = (Lookup<int, string>)
             (new Dictionary<int, string>() { {1, "one"} })
             .ToLookup(p=>p.Key, p=>p.Value)
      }  
      

      不过,我不明白您为什么要在此处使用查找而不是字典。

      【讨论】:

      • 查找可以包含重复的键并保持顺序。这与字典明显不同。此外,除了整个不变的东西,虽然有 一些 等效的地图实现.. 它不是字典。
      • @Gabe 他想使用字典中不可能的一对多查找映射
      【解决方案6】:

      查找与字典使用相同的概念,不同之处在于字典将键映射到单个值,而查找将键映射到多个值

      这也意味着:

      ILookup<string, Category>
      

      可以看作:

      IDictionary<string, IEnumerable<Category>>
      

      当您想要将许多对象/值映射到同一个键时,您基本上会想要使用ILookup。您可以从任何对象列表构建ILookup,您希望在其中按某些属性对这些对象进行分组。见:

      public class Product
      {
          public string Name { get; set; }
          public string Category { get; set; }
          public decimal Price { get; set; }
      }
      
      var products = new List<Product>();
      products.Add(new Product { Name = "TV", Price = 400, Category = "Electronics" });
      products.Add(new Product { Name = "Computer", Price = 900, Category = "Electronics" });
      products.Add(new Product { Name = "Keyboard", Price = 50, Category = "Electronics" });
      products.Add(new Product { Name = "Orange", Price = 2, Category = "Fruits" });
      products.Add(new Product { Name = "Grape", Price = 3, Category = "Fruits" });
      
      // group by category
      ILookup<string, Product> lookup = products.ToLookup(prod => prod.Category);
      
      foreach (var item in lookup)
      {
          // this first loop would run two times
          // because there are two categories: Electronics and Fruits
          string category = item.Key;
          decimal totalPriceForCategory = item.Sum(i => i.Price);
      
          foreach (var product in item)
          {
              // for the electronics, this would loop three times
              // for the fruits, this would loop two times
              string name = product.Name;
              decimal price = product.Price;
          }
      }
      

      您还可以像这样获取一个类别的所有产品:

      IEnumerable<Product> eletronics = lookup["Electronics"];
      IEnumerable<Product> fruits = lookup["Fruits"];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多