Много-много-много фильтров - PullRequest
1 голос
/ 30 января 2009

Сегодня мы столкнулись с довольно простой проблемой, которая стала еще проще благодаря дорогим предикатам. У нас был своего рода журнал событий, и мы хотели отфильтровать его на стороне клиента (Windows Forms), используя список критериев. Мы начали с внедрения фильтра по ряду категорий.

private List<Events> FilterEventsByCategory(List<Events> events,
                                        List<Category> categories) 
{
  return events.FindAll(ev => 
      categories.Exists(category => category.CategoryId==ev.CategoryId)); 
}

Следующим шагом является внедрение нескольких других фильтров. Знаете ли вы о хорошем способе их обобщить, чтобы, возможно, каким-то образом не приходилось писать по одному методу на фильтр? Или, по крайней мере, чистый способ иметь динамический список фильтров, которые мы хотим применить одновременно.

Клиенты по-прежнему работают на платформе 3.0, поэтому LINQ отсутствует.

Обновление: Мне было трудно решить, кому я должен отдать должное за мое решение. У Марка было несколько хороших идей, и он действительно хорош в их объяснении. Скорее всего, я бы получил от него ответ, если бы только немного лучше объяснил свою проблему. В конечном счете, это был общий класс Filter, предоставленный cmartin, который помог мне в этом. Класс Filter, используемый ниже, можно найти в ответе cmartins, а также в классе User, который вы можете придумать сами.

var categoryFilter = new Filter<Event>(ev => categories.Exists(category => category.CategoryId == ev.CategoryId));
var userFilter = new Filter<Event>(ev => users.Exists(user => user.UserId == ev.UserId));

var filters = new List<Filter<Event>>();
filters.Add(categoryFilter);
filters.Add(userFilter);

var eventsFilteredByAny = events.FindAll(ev => filters.Any(filter => filter.IsSatisfied(ev)));
var eventsFilteredByAll = events.FindAll(ev => filters.All(filter => filter.IsSatisfied(ev)));

Ответы [ 2 ]

2 голосов
/ 30 января 2009

'так нет LINQ' - вы смотрели на LINQBridge ? Поскольку вы используете C # 3.0, это было бы идеально ...

Боюсь за главный вопрос, я не до конца понимаю, что вы пытаетесь сделать и чего вы пытаетесь избежать - вы можете уточнить вообще? Но если вы используете подход LINQBridge, вы можете комбинировать фильтры, используя последовательные .Where() вызовы.

Одна из интерпретаций вопроса заключается в том, что вам не нужно много методов фильтрации - так что, возможно, передайте в метод один или несколько дополнительных предикатов - по существу, как Func<Event, Func<Category, bool>> - или в чистых терминах 2.0, Converter<Event, Predicate<Category>> :

private static List<Events> FilterEvents(
    List<Events> events,
    List<Category> categories,
    Converter<Events, Predicate<Category>> func)
{
    return events.FindAll(evt =>
        categories.Exists(func(evt)));
}

, который затем используется (как указано выше) как:

var result = FilterEvents(events, categories,
      evt => category => category.CategoryId==evt.CategoryId);
1 голос
/ 30 января 2009

Вот очень простой пример того, с чего я бы начал.

internal class Program
{
    private static void Main()
    {
        var ms = new Category(1, "Microsoft");
        var sun = new Category(2, "Sun");

        var events = new List<Event>
                         {
                             new Event(ms, "msdn event"),
                             new Event(ms, "mix"),
                             new Event(sun, "java event")
                         };

        var microsoftFilter = new Filter<Event>(e => e.CategoryId == ms.CategoryId);

        var microsoftEvents = FilterEvents(events, microsoftFilter);

        Console.Out.WriteLine(microsoftEvents.Count);
    }

    public static List<Event> FilterEvents(List<Event> events, Filter<Event> filter)
    {
        return events.FindAll(e => filter.IsSatisfied(e));
    }
}

public class Filter<T> where T: class
{
    private readonly Predicate<T> criteria;

    public Filter(Predicate<T> criteria)
    {
        this.criteria = criteria;
    }

    public bool IsSatisfied(T obj)
    {
        return criteria(obj);
    }
}

public class Event
{
    public Event(Category category, string name)
    {
        CategoryId = category.CategoryId;
        Name = name;
    }

    public int CategoryId { get; set; }
    public string Name { get; set; }
}

public class Category
{
    public Category(int categoryId, string name)
    {
        CategoryId = categoryId;
        Name = name;
    }

    public string Name { get; set; }
    public int CategoryId { get; set; }
}
...