【问题标题】:C# Custom Collection / EnumeratorC# 自定义集合/枚举器
【发布时间】:2012-07-01 02:40:11
【问题描述】:

给定以下类:

public class GenClass<T>
{
     private List<T> ItemsList {get;set;}
     public Predicate<T> SomeCondition {get;set;}
     public bool UsePredicate {get;set;}

     public List<T> Items
     {
         get { //CODE Goes here; }
     }
}

我需要一种方法让列表使用SomeConditionPredicate 并仅返回与条件匹配的项目,但前提是布尔UsePredicate 为真。我知道我可以为此使用 LINQ,问题是每次我使用 LINQ 查询时都会得到一个不同的 IEnumerable 实例,这需要是一个属性,因此我需要能够访问列表的同一个实例从课外,因为我将添加和删除项目,而我不能用 .Where 的结果来做到这一点,例如。 我在想一个自定义的IList&lt;T&gt;,但我不太确定该怎么做。

【问题讨论】:

  • 如何将 Enumerable 成员设为静态?
  • 那么为什么获取不同的 IEnumerable 会成为问题?
  • 这是一个问题,因为如果我从类外部执行 GenClass.Items.Add(xxx),则新项目永远不会添加到真正的列表中,因为 GenClass.Items 不是列表本身,而是LinQ 返回的不同 IEnumerable。

标签: c# generics collections


【解决方案1】:

这里有一个概念问题。如果它是同一个实例,它应该如何按条件过滤? LINQ 在每次调用时都返回一个新枚举的原因是它“实时”运行查询,并且多个查询必须是独立的。

也就是说,您可能不必依赖每次返回相同引用的属性。如果您依赖于相同的实例,当/如果有人更改谓词时,您期望会发生什么?

添加或删除项目如何在过滤列表上起作用/起作用?如果您添加一个会被过滤掉的项目,会发生什么?

【讨论】:

  • 你是对的。我没有想到这一点。应该使用方法将新项目添加到集合中吗?如果我添加一个不符合条件的项目,无论如何我都需要该项目成为内部 ItemsList 的一部分,因为当我关闭 UsePredicate 标志时,我需要返回所有项目。
【解决方案2】:

你的问题有点不清楚,但让我们从这个开始:

public class GenClass<T>
{
     private List<T> ItemsList {get;set;}
     public Predicate<T> SomeCondition {get;set;}
     public bool UsePredicate {get;set;}

     public List<T> Items
     {
         get { return UsePredicate 
                   ? ItemsList.Where(SomeCondition).ToList() 
                   : ItemsList; }
     }
}

那不适合您的用例怎么办?

【讨论】:

    【解决方案3】:

    IEnumerable 只能用于读取集合,但不能对其进行更改。如果要对其进行更改,请改为返回过滤索引的枚举。

    public IEnumerable<int> FilteredIndexes
    {
        get
        {
            if (UsePredicate) {
                return ItemsList
                    .Select((item, i) => i)
                    .Where(i => SomeCondition(ItemsList[i]));
            }
            return ItemsList.Select((item, i) => i);
        }
    }
    

    假设你已经声明了这个索引器

    public T this[int index]
    {
        get { return ItemsList[index]; }
        set { ItemsList[index] = value; }
    }
    

    您现在可以像这样使用该集合

    GenClass<string> stringCollection = new GenClass<string>();
    //TODO: Add items
    stringCollection.SomeCondition = s => s.StartsWith("A");
    stringCollection.UsePredicate = true;
    foreach (int index in stringCollection.FilteredIndexes) {
        stringCollection[index] = stringCollection[index] + " starts with 'A'";
    }
    

    更新

    如果您不想公开索引,您可以创建一个类用作表示您的集合项目的项目访问器

    public class Item<T>
    {
        private List<T> _items;
        private int _index;
    
        public Item(List<T> items, int index)
        {
            _items = items;
            _index = index;
        }
    
        public T Value
        {
            get { return _items[_index]; }
            set { _items[_index] = value; }
        }
    }
    

    在您的收藏中,您将声明此属性

    public IEnumerable<Item<T>> FilteredItems
    {
        get
        {
            if (UsePredicate) {
                return ItemsList
                    .Select((item, i) => new Item<T>(ItemsList, i))
                    .Where(item => SomeCondition(item.Value));
            }
            return ItemsList.Select((item, i) => new Item<T>(ItemsList, i));
        }
    }
    

    现在你可以像这样使用集合了

    foreach (Item<string> item in stringCollection.FilteredItems) {
        item.Value = item.Value + " starts with 'A'";
    }
    

    一般注意事项:您可以安全地将私有属性转换为字段。属性通常用作公开公开字段值的中间体。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多