我正在尝试创建一个集合,它的作用与所有其他集合一样,但当您读出它时,可以通过设置一个标志来过滤它。
考虑一下这个(诚然是人为的)例子:
var list = new FilteredStringList() {
"Annie",
"Amy",
"Angela",
"Mary"
};
list.Filter = true;我添加了四个条目,但随后设置了" filter“标志,这样每当我从其中读取任何内容(或者做任何涉及读取的事情)时,我都希望将列表过滤为以"A”开头的项。
这是我的课:
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迭代,我得到:
Annie
Amy
Angela耶!但是,如果我这么做:
list.Count()我得到"4“。所以,Count()不尊重我的过滤器。
如果我这么做
list.Where(i => i[1] == 'a')我还是会把“玛丽”还给你,尽管她不应该在那里。
我知道我可以覆盖所有的Collection方法,但是我并不想完全覆盖它们。此外,我希望所有LINQ方法尊重我的过滤器。
发布于 2013-12-24 20:05:31
听起来你的概念是有一个列表,可以用两种不同的方式来表示。这实际上是两个列表,我建议将其作为两个列表来运行。进一步发展你的人为的例子:
// the base list containing all items
var list = new List<string>
{
"Annie", "Amy", "Angela", "Mary"
};然后,创建该列表的表达式,该表达式仅为筛选的项:
// do NOT .ToList() this list:
var filteredList = list.Where (x => x.StartsWith("A"));您可以继续从list中添加/删除项,并且在访问filteredList时,filteredList将始终包含更新的值。
如果您真的必须只访问一个字段/属性,那么可以将list和filteredList放入一个类中,该类还包含一个IsFiltered属性,用于确定返回这两个列表中的哪一个。
您要做的是创建一个返回正确列表的类。类似于:
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);
发布于 2013-12-24 20:04:33
不要继承Collection。相反,只需直接从Object继承,让方法实现所需的任何接口(可能是IList),创建一个内部List实例作为支持集合,然后按您希望看到的那样公开每个操作。
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
}这里有几个优点。
List方法,这是一个激励因素。要真正表示过滤后的列表,几乎每一个操作都可能会发生变化,所以这并不意味着您要为自己添加一堆工作。Collection的方法不是虚拟的,它们是密封的,这意味着将类键入为基类将导致使用基类方法。这意味着当您认为启用了过滤器时,调用方可以绕过您要添加的筛选器。听起来真是个坏主意。我的理论是,我正在重写和过滤基本集合对象中的Items属性。我认为所有其他方法都会从这个集合中读取。
但你并没有凌驾于Items之上,而是在隐藏它。您甚至需要通过使用new关键字而不是override关键字来调用这样一个事实:您没有重写它。即使您想使用override关键字,也不能使用它,因为Collection中的所有方法/属性都是sealed,而不是virtual。它是专门用来阻止你做你想做的事的。(这就是为什么我通常认为它作为一个类没有什么用处。)
我知道我可以覆盖所有的Collection方法,但是我并不想完全覆盖它们。
从技术上讲,您必须覆盖的唯一虚拟方法是ClearItems、InsertItem、RemoveItem和SetItem (以及Object中的Equals、GetHashCode和ToString )。其余的方法/属性不是虚拟的,因此不能被覆盖。唯一真正有效的解决办法是把它们都写出来。
发布于 2013-12-24 22:48:58
为什么不只在需要时使用普通的旧List并应用Where子句?
var list = new List<string>() {
"Annie",
"Amy",
"Angela",
"Mary"
};
list.Where(o=>o.StartsWith("A")).Count();https://stackoverflow.com/questions/20765907
复制相似问题