枚举表达式列表以过滤集合
Posted
技术标签:
【中文标题】枚举表达式列表以过滤集合【英文标题】:Enumerating through list of expressions to filter collection 【发布时间】:2012-08-19 09:14:15 【问题描述】:我在这里挑战了我在 c# 和 linq 方面的知识极限,所以如果我完全不理解我的示例或对 linq、c#、泛型类型、lambda 表达式、设计模式等的理解,请多多包涵。
我有一个包含两个集合的类,一个是要过滤的集合:IEnumberable<InstagramUser>
,第二个是要过滤的表达式集合:IEnumerable<IInstagramFilter>
。
public class InstagramDisplay
public IEnumerable<InstagramUser> instagramUsers;
public IEnumerable<IInstagramFilter> instagramFilters;
public InstagramDisplay()
instagramUsers = new List<InstagramUser>();
instagramFilters = new List<IInstagramFilter>();
public IEnumerable<InstagramUser> display()
instagramFilters.ToList().ForEach(x => instagramUsers.Where(x.filter(instagramUsers)));
return instagramFilters;
public interface IInstagramFilter
Expression<Func<T, bool>> filter<T>(IQueryable<T> source);
我会让课程扩展IInstagramFilter
。每个IInstagramFilter
类都有一个属性(或函数——不确定什么是最好的),它将返回将在display()
方法中应用于IEnumerable<InstagramUser>
的lambda 表达式。
public class UserFilter : IInstagramFilter
public Expression<Func<T, bool>> filter<T>(IQueryable<T> source)
//return some expression - but how?
我很难理解一些事情:
如何为每个IInstagramFilter
类设置表达式,然后在display()
方法中调用?
每个IInstagramFilter
类都有一个用于过滤IEnumerable<InstagramUser>
的lambda,但由于Filter 类不知道IEnumerable<InstagramUser>
,我如何首先创建合适的lambda?
我认为这大致遵循装饰器模式,但也许有一个更好的设计,我不知道。
更新代码
根据 Olivier 的回答,这就是我现在所拥有的。在返回 display()
时,我在使用 .Where(filter)
时遇到错误
方法“
System.Linq.Enumerable.Where<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,bool>)
”的类型参数无法从用法中推断出来。尝试明确指定类型参数。
public class InstagramDisplay
public IEnumerable<InstagramUser> instagramUsers;
public List<Expression<Func<InstagramUser, bool>>> instagramFilters;
public InstagramDisplay()
instagramUsers = new List<InstagramUser>();
instagramFilters = new List<Expression<Func<InstagramUser, bool>>>();
public void addFilter(Expression<Func<InstagramUser, bool>> filter)
instagramFilters.Add(filter);
public IEnumerable<InstagramUser> display()
return instagramFilters.SelectMany(filter => instagramUsers.Where(filter)).Distinct(); //error on this line
【问题讨论】:
【参考方案1】:您必须决定是要执行某些操作还是要返回某些内容。 List<T>.ForEach()
对每个项目执行一个操作,但返回类型为 void,不返回任何内容。
这个IInstagramFilter
界面对我来说似乎是多余的。你可以像这样声明一个过滤器列表
var userFilters = new List<Expression<Func<InstagramUser, bool>>>();
userFilters.Add(u => u.Name.StartsWith("A"));
userFilters.Add(u => u.Score >= 100);
如果您有用户来源,您可以对所有过滤器返回的所有用户执行类似操作
IQueryable<InstagramUser> usersSource = ...;
// Or IEnumerable<InstagramUser> for LINQ to objects
// if you drop the Expression<> part.
var users = userFilters.SelectMany(f => usersSource.Where(f));
SelectMany
将嵌套的枚举展平。该示例返回名称以“A”开头或得分 >= 100 的所有用户。这可能会返回一个用户两次,因此我建议附加 .Distinct()
var users = userFilters
.SelectMany(f => usersSource.Where(f))
.Distinct();
【讨论】:
我同意这里不需要IInstagramFilter
。而不是SelectMany
,他不能循环过滤器集合,将.Where(filter)
附加到userSource
。这将防止重复用户,并使用“and”而不是“or”将过滤器链接在一起
@MikeC:将过滤器链接在一起是一个非常好的主意,因为它使Distinct()
变得多余。我不确定 OP 是否希望他们进行 ORed 或 ANDed(从他的示例中我假设 ORed)。 AND 很简单(只需链接Wheres
,OR 更难。
@OlivierJacot-Descombes 这是一个很好的答案。我已经在上面发布了我的更新代码,但在display()
的返回语句中仍然出现错误
C# 擅长推断类型参数,但在少数情况下它无法做到这一点。然后你必须明确指定它们return instagramFilters.SelectMany<Expression<Func<InstagramUser, bool>>, InstagramUser>(filter => instagramUsers.Where(filter)).Distinct();
。
@OlivierJacot-Descombes 我必须使用Expression<Func<>>
吗?执行 .Where(filter)
时出现错误,但如果我将 instagramFilters
更改为 `List以上是关于枚举表达式列表以过滤集合的主要内容,如果未能解决你的问题,请参考以下文章
如何过滤 label_values(label) 的结果以获取与正则表达式匹配的标签列表?