首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何扩展集合以提供筛选过的对象集合?

如何扩展集合以提供筛选过的对象集合?
EN

Stack Overflow用户
提问于 2013-12-24 19:28:38
回答 4查看 1.9K关注 0票数 1

我正在尝试创建一个集合,它的作用与所有其他集合一样,但当您读出它时,可以通过设置一个标志来过滤它。

考虑一下这个(诚然是人为的)例子:

代码语言:javascript
复制
var list = new FilteredStringList() {
    "Annie",
    "Amy",
    "Angela",
    "Mary"
};
list.Filter = true;

我添加了四个条目,但随后设置了" filter“标志,这样每当我从其中读取任何内容(或者做任何涉及读取的事情)时,我都希望将列表过滤为以"A”开头的项。

这是我的课:

代码语言:javascript
复制
public class FilteredStringList : Collection<string>
{
    public bool Filter { get; set; }

    protected new IList<string> Items
    {
        get
        {
            if(Filter)
            {
                return base.Items.Where(i => i.StartsWith("A")).ToList();
            }
            return base.Items;
        }
    }

    public IEnumerator GetEnumerator()
    {
        foreach(var item in Items)
        {
            yield return item;
        }
    }
}

我的理论是,我正在重写和过滤基本集合对象中的Items属性。我认为所有其他方法都会从这个集合中读取。

如果我通过ForEach迭代,我得到:

代码语言:javascript
复制
Annie
Amy
Angela

耶!但是,如果我这么做:

代码语言:javascript
复制
list.Count()

我得到"4“。所以,Count()不尊重我的过滤器。

如果我这么做

代码语言:javascript
复制
list.Where(i => i[1] == 'a')

我还是会把“玛丽”还给你,尽管她不应该在那里。

我知道我可以覆盖所有的Collection方法,但是我并不想完全覆盖它们。此外,我希望所有LINQ方法尊重我的过滤器。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-12-24 20:05:31

听起来你的概念是有一个列表,可以用两种不同的方式来表示。这实际上是两个列表,我建议将其作为两个列表来运行。进一步发展你的人为的例子:

代码语言:javascript
复制
// the base list containing all items
var list = new List<string>
{
    "Annie", "Amy", "Angela", "Mary"
};

然后,创建该列表的表达式,该表达式仅为筛选的项:

代码语言:javascript
复制
 // do NOT .ToList() this list:
 var filteredList = list.Where (x => x.StartsWith("A"));

您可以继续从list中添加/删除项,并且在访问filteredList时,filteredList将始终包含更新的值。

如果您真的必须只访问一个字段/属性,那么可以将listfilteredList放入一个类中,该类还包含一个IsFiltered属性,用于确定返回这两个列表中的哪一个。

您要做的是创建一个返回正确列表的类。类似于:

代码语言:javascript
复制
public class Filtered<T>
{
    private IEnumerable<T> _list;
    private IEnumerable<T> _filteredList;

    public bool IsFiltered { get; set; }

    public IEnumerable<T> MyCollection { get { return IsFiltered ? _filteredList 
                                                                 : _list; }}

    public Filtered(IEnumerable<T> list, IEnumerable<T> filteredList)
    {
        _list = list;
        _filteredList = filteredList;
    }
}

而不是像最初的示例那样绑定到list,而是绑定到上面类的MyCollection属性(filtered.MyCollection);

票数 1
EN

Stack Overflow用户

发布于 2013-12-24 20:04:33

不要继承Collection。相反,只需直接从Object继承,让方法实现所需的任何接口(可能是IList),创建一个内部List实例作为支持集合,然后按您希望看到的那样公开每个操作。

代码语言:javascript
复制
public class FilteredCollection : IList<string>, IEnumerable<string>
{
    private List<string> list = new List<string>();
    public bool Filter { get; set; }

    protected IList<string> Items
    {
        get
        {
            if (Filter)
            {
                return list.Where(i => i.StartsWith("A")).ToList();
            }
            return list;
        }
    }

    public IEnumerator<string> GetEnumerator()
    {
        return Items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public int IndexOf(string item)
    {
        throw new NotImplementedException();
    }

    public void Insert(int index, string item)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    public string this[int index]
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    //continue with the rest
}

这里有几个优点。

  1. 你很清楚每种方法都在做什么。您不需要担心方法会留下一些不合理的缺省值。事实上,只有少数几个操作可能只是简单地调用相同的List方法,这是一个激励因素。要真正表示过滤后的列表,几乎每一个操作都可能会发生变化,所以这并不意味着您要为自己添加一堆工作。
  2. Collection的方法不是虚拟的,它们是密封的,这意味着将类键入为基类将导致使用基类方法。这意味着当您认为启用了过滤器时,调用方可以绕过您要添加的筛选器。听起来真是个坏主意。

我的理论是,我正在重写和过滤基本集合对象中的Items属性。我认为所有其他方法都会从这个集合中读取。

但你并没有凌驾于Items之上,而是在隐藏它。您甚至需要通过使用new关键字而不是override关键字来调用这样一个事实:您没有重写它。即使您想使用override关键字,也不能使用它,因为Collection中的所有方法/属性都是sealed,而不是virtual。它是专门用来阻止你做你想做的事的。(这就是为什么我通常认为它作为一个类没有什么用处。)

我知道我可以覆盖所有的Collection方法,但是我并不想完全覆盖它们。

从技术上讲,您必须覆盖的唯一虚拟方法是ClearItemsInsertItemRemoveItemSetItem (以及Object中的EqualsGetHashCodeToString )。其余的方法/属性不是虚拟的,因此不能被覆盖。唯一真正有效的解决办法是把它们都写出来。

票数 0
EN

Stack Overflow用户

发布于 2013-12-24 22:48:58

为什么不只在需要时使用普通的旧List并应用Where子句?

代码语言:javascript
复制
var list = new List<string>() {
    "Annie",
    "Amy",
    "Angela",
    "Mary"
};

list.Where(o=>o.StartsWith("A")).Count();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20765907

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档