枚举表达式列表以过滤集合

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&lt;InstagramUser&gt; 的lambda 表达式。

public class UserFilter : IInstagramFilter 
    public Expression<Func<T, bool>> filter<T>(IQueryable<T> source) 
        //return some expression - but how?
    

我很难理解一些事情:

    如何为每个IInstagramFilter类设置表达式,然后在display()方法中调用?

    每个IInstagramFilter 类都有一个用于过滤IEnumerable&lt;InstagramUser&gt; 的lambda,但由于Filter 类不知道IEnumerable&lt;InstagramUser&gt;,我如何首先创建合适的lambda?

    我认为这大致遵循装饰器模式,但也许有一个更好的设计,我不知道。

更新代码

根据 Olivier 的回答,这就是我现在所拥有的。在返回 display() 时,我在使用 .Where(filter) 时遇到错误

方法“System.Linq.Enumerable.Where&lt;TSource&gt;(System.Collections.Generic.IEnumerable&lt;TSource&gt;, System.Func&lt;TSource,bool&gt;)”的类型参数无法从用法中推断出来。尝试明确指定类型参数。

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&lt;T&gt;.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&lt;Expression&lt;Func&lt;InstagramUser, bool&gt;&gt;, InstagramUser&gt;(filter =&gt; instagramUsers.Where(filter)).Distinct(); @OlivierJacot-Descombes 我必须使用Expression&lt;Func&lt;&gt;&gt; 吗?执行 .Where(filter) 时出现错误,但如果我将 instagramFilters 更改为 `List> 则我可以编译。我是否真的过滤了我不确定的列表。我以为我明白在将表达式树作为 lambda 传递给 linq 时必须使用表达式树,我错了吗?

以上是关于枚举表达式列表以过滤集合的主要内容,如果未能解决你的问题,请参考以下文章

如何过滤 label_values(label) 的结果以获取与正则表达式匹配的标签列表?

Java Enum枚举 遍历判断 四种方式(包括 Lambda 表达式过滤)

『Python』高阶特性

python高级特性

Java集合与数据结构 反射枚举及Lambda表达式

Java集合与数据结构 反射枚举及Lambda表达式