【问题标题】: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?
    }
}

我很难理解一些事情:

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

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

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

更新代码

根据 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
    }
}

【问题讨论】:

    标签: c# linq lambda expression


    【解决方案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 时必须使用表达式树,我错了吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多