Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.97/29: Рейтинг темы: голосов - 29, средняя оценка - 4.97
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223

Конструктор фильтра

03.04.2018, 12:30. Показов 6416. Ответов 37
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день!
Хочу сделать что-то вроде универсального метода - конструктора фильтра.
В качестве входных параметров предполагается:
1. List<string> PropNames - коллекция имен свойств. Есть свойства, которые присущи нескольким классам. Сами свойства есть экземпляры классов-справочников, которые наследуются от базового класса
C#
1
2
3
4
5
    public class Entity
    {
        public virtual int ID {get; set;}
        public virtual string Description { get; set; }
    }
2. List <List<int>> Values - коллекция допустимых ID для фильтрации коллекции некоего класса (не класс-справочника) по его свойствам.

3. List<SomeEntity> InList - коллекция, предполагаемая к фильтрованию.

Выходные параметры:
1. List<SomeEntity> OutList - отфильтрованная коллекция.

Не знаю с какой стороны подступиться к решению. Готов выслушать любые предложения/идеи.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
03.04.2018, 12:30
Ответы с готовыми решениями:

Определить конструктор без параметров, конструктор с одним параметром, конструктор с двумя параметрами, деструктор
Создать класс «матрица». Данный класс должен содержать элементы типа int, определяющие число строк, столбцов и состояние ошибки. Определить...

Ошибка при наложении программного фильтра и фильтра на форме
Добрый день, на форме программно устанавливается фильтр записей - все хорошо. но когда средствами аксесс формы дополнительно пытаешься...

Какой метод синтеза фильтра обеспечивает наименьшее число параметров фильтра при заданных ограничениях АЧХ?
Доброго времени суток! Подскажите пожалуйста,какой метод синтеза фильтра обеспечивает наименьшее кол-во параметров фильтра при заданных...

37
 Аватар для Cupko
658 / 595 / 171
Регистрация: 17.07.2012
Сообщений: 1,682
Записей в блоге: 1
03.04.2018, 12:38
wrq2017, тут вам помогут деревья выражений (Expression Trees) и класс Expression + Linq для фильтрации коллекции: строите Expression, компилируете в предикат и скармливаете его в тот же Enumerable.Where().
1
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
03.04.2018, 12:39  [ТС]
Cupko, примером не поможете?
0
 Аватар для Cupko
658 / 595 / 171
Регистрация: 17.07.2012
Сообщений: 1,682
Записей в блоге: 1
03.04.2018, 12:49
wrq2017, мы мыслим концептуально

Это не такая простая тема, чтобы сходу под ваши нужды пример забабахать, а под рукой ничего готового нет. Придется вам искать/читать/разбираться.
1
.NET C#,ASP.NET MVC
Эксперт .NET
 Аватар для lvlkoo
594 / 507 / 224
Регистрация: 16.10.2010
Сообщений: 1,902
03.04.2018, 13:52
Лучший ответ Сообщение было отмечено wrq2017 как решение

Решение

1. Читаем все про Expression и System.Linq.Expressions
2. Планируем модель вводных данных, которая должна содержать в себе свойство "PropertyName" с названием свойства по которому будет фильтрация, "Value" - значение\значения с которым сранивать, и перечесление "Operation", которое будет содрежать в себе список возможных операция над выражениями (Equal,Greater,Lower итд)
3. Начинаем строить с определения ParameterExpression
C#
1
var paramExp = Expression.Parameter(typeof(T), "x");
Затем нужно получить MemberExpression для нужного "PropertyName"
C#
1
var propExp = Expression.Property(paramExp, "PropertyName");
Далее нужно сформировать выражение-константу из полученого Value
C#
1
var constExp = Expression.Constant(value);
Затем формируем само выражение на основе операции
C#
1
2
3
4
5
6
7
8
 switch (operation)
{
    case Operation.Equal:
        var resultExp = Expression.Equal(propExp, constantExp);
    case Operation.Greater:
        var resultExp = Expression.GreaterThan(propExp, constantExp);
    //и так далее
}
в результате этого вы получите выражение типо такого
Code
1
x => x.PropertyName == MyValue
Далее можете вставлять его в предикат

4. Информация для размышления:
для получения списка всех возможных операций - читайте про Expression
выражения можно комбинировать через Expression.And (exp1, exp2) или Expression.Or(exp1, exp2) - сооствественно будет строиться нужная логическая цепочка.
Expression.Call - позволяет использовать методы в выражениях через MethodInfo, который можно получить с помощью рефлексии (типо Contains, Any итд)
3
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
03.04.2018, 14:09  [ТС]
lvlkoo, огромное спасибо! примерно так уже и начал делать.

У меня какая-то проблема со вставкой в предикат выражения.

Делаю так:
C#
1
List<Party> filterList = SomeList.Where<Party>(ExprTree);
Получаю ошибку : 'List<Party>" не содержит определение для "Where", и наиболее подходящий перегруженный метод расширения "Queryable.Where<Party>(IQueryable<Party >, Expression<Func<Party, bool>>)" требует наличия получателя типа "IQueryable<Party>".

Что я делаю не так?
0
 Аватар для Cupko
658 / 595 / 171
Регистрация: 17.07.2012
Сообщений: 1,682
Записей в блоге: 1
03.04.2018, 14:13
wrq2017, Expression нужно скомпилить для начала, я предполагаю.
1
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
04.04.2018, 10:03  [ТС]
Cupko, ааааа.... Вон оно чё.... Спасибо!!!!

Добавлено через 19 часов 47 минут
lvlkoo, позвольте добить тему до конца. В общем всё понятно, а на конкретном примере не работает.

Для примера, хочу отфильтровать коллекцию экземпляров класса Party, у которого есть поле Contract. И я хочу найти все Party, у которых Contract.Id = 5.
Делаю так:
C#
1
2
3
4
5
6
7
8
9
10
11
            ParameterExpression param = Expression.Parameter(typeof(Party), "x");
            int[] ids = new int[] { 5 };
            Expression<Func<Party, bool>> aLambda = Expression.Lambda<Func<Party, bool>>(
                Expression.Equal(Expression.Call(
                Expression.Property(param, "Contract"),
                typeof(int[]).GetMethod("Contains"),
                new Expression[] { Expression.Constant(ids, ids.GetType()) }),
                Expression.Constant(true)),
                param);
            var deleg = aLambda.Compile();
            List<Party> filterList = Parties.Where(deleg).ToList();
Получаю ошибку: "Значение не может быть неопределенным. Имя параметра: method". Чувствую, что я и по основной логике делаю что-то не так. Как сделать правильно?
0
 Аватар для Cupko
658 / 595 / 171
Регистрация: 17.07.2012
Сообщений: 1,682
Записей в блоге: 1
04.04.2018, 10:30
wrq2017,
у массива нет метода Contains, я полагаю вам нужен List<T>.Contains() или Enumerable.Contains().
Вынесли бы отдельно получение MethodInfo - было б понятнее
0
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
04.04.2018, 10:37  [ТС]
Eсли делать проще, без рефлексии и использования метода Contains, в этом случае получаем ошибку: Двоичный оператор Equal не определен для типов "Sales.Data_model.Contract" и "System.Int32"."
C#
1
2
3
4
5
6
              ParameterExpression pe = Expression.Parameter(typeof(Party), "x");
              MemberExpression me = Expression.Property(pe, "Contract");
              ConstantExpression constant = Expression.Constant(5, typeof(int));
              BinaryExpression body = Expression.Equal(me, constant);
              var resultExp = Expression.Lambda<Func<Party, bool>>(body, new[] { pe });
              var deleg = resultExp.Compile();
А в этом случае получаем ошибку: Свойство экземпляра "Contract.Id" не определено для типа "Sales.Data_model.Party

C#
1
2
3
4
5
6
              ParameterExpression pe = Expression.Parameter(typeof(Party), "x");
              MemberExpression me = Expression.Property(pe, "Contract.Id");
              ConstantExpression constant = Expression.Constant(5, typeof(int));
              BinaryExpression body = Expression.Equal(me, constant);
              var resultExp = Expression.Lambda<Func<Party, bool>>(body, new[] { pe });
              var deleg = resultExp.Compile();
Добавлено через 3 минуты
Cupko, делаю как написано тут.
C#
1
2
3
4
5
6
7
MethodInfo ContainsOffOfIEnumerable = OverloadedMethodFinder.FindOverloadedMethodToCall("Contains", typeof(Enumerable), typeof(IEnumerable<Party>), typeof(Party)).MakeGenericMethod(typeof(Party));
            ParameterExpression param = Expression.Parameter(typeof(Party), "x.Contract");
            ConstantExpression ConstantListExpressionParameter = Expression.Constant(Parties, typeof(IEnumerable<Party>));
            MethodCallExpression ContainsMethodCall = Expression.Call(ContainsOffOfIEnumerable, ConstantListExpressionParameter, param);
            Expression<Func<Party, bool>> aLambda = Expression.Lambda<Func<Party, bool>>(ContainsMethodCall, param);
            var deleg = aLambda.Compile();
            List<Party> filterList = Parties.Where(deleg).ToList();
Ошибок по ходу выполнения нет, но результат какой-то непонятный.

Добавлено через 1 минуту
Цитата Сообщение от Cupko Посмотреть сообщение
у массива нет метода Contains, я полагаю вам нужен List<T>.Contains() или Enumerable.Contains().
Вынесли бы отдельно получение MethodInfo - было б понятнее
Менял int[] на Enumerable и на List<Party> - ошибка та же: "Значение не может быть неопределенным. Имя параметра: method"
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
04.04.2018, 11:00
wrq2017,
Мне кажется, ExpressionTree для вашего случая это лютый over engineering. Зачем так усложнять?
Объясните более подробно что должен делать ваш метод. Откуда берутся выражения для фильтрации, что в них будет, зачем все это нужно?
1
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
04.04.2018, 11:47  [ТС]
Storm23, не знаю лютый или не лютый Подход мне нравится, надо только разобраться.
Есть несколько коллекций классов, которые предполагается фильтровать. У каждого такого класса есть свойства, типы свойств - либо другие классы (вроде словарей), либо DateTime.

Выражения для фильтрации по сути два:
1. проверить содержится ли Id какого-либо свойства в массиве (коллекции) допустимых.
2. Если тип свойства DateTime, то оно должно входить в допустимый диапазон дат.

В настоящий момент для каждого фильтруемого класса написан отдельный метод, в котором вручную последовательно определены свойства и передача в качестве параметра коллекции допустимых значений.

Всё бы хорошо, но сейчас достаточно неудобно отслеживать - все ли свойства учтены в том или ином методе, неудобно добавлять метод к новому классу (нарушает принцип DRY?). Хотелось бы сделать этот процесс универсальнее и проще.

Добавлено через 1 минуту
Storm23, есть альтернативные предложения?
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
04.04.2018, 13:36
Цитата Сообщение от wrq2017 Посмотреть сообщение
не знаю лютый или не лютый Подход мне нравится, надо только разобраться.
ExpressionTree предназначены для тех случаев, когда выражения/формулы/алгоритмы заранее неизвестны. По сути это динамическая компиляция кода. Оно то конечно хорошо, но для динамического построения ExpressionTree (например если условия отбора вводятся пользователем в текстовом поле) вам придется делать синтаксический анализ введенных выражений и по ним уже строить ExpressionTree, что достаточно сложно.
Если же выражения для фильтрации заранее известны - ExpressionTree это лишнее переусложнение.

Цитата Сообщение от wrq2017 Посмотреть сообщение
Выражения для фильтрации по сути два:
1. проверить содержится ли Id какого-либо свойства в массиве (коллекции) допустимых.
2. Если тип свойства DateTime, то оно должно входить в допустимый диапазон дат.
Делаем простой класс-расширение:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public static class FilterHelper
    {
        public static IEnumerable Where<T>(this IEnumerable list, string propertyName, Func<T, bool> filter)
        {
            foreach(var obj in list)
            if (obj != null)
            {
                var pi = obj.GetType().GetProperty(propertyName);
                if (pi != null && pi.PropertyType == typeof(T))
                if (filter((T)pi.GetValue(obj)))
                    yield return obj;
            }
        }
    }
Метод Where принимает список, проверяет наличие свойства с данным именем и данного типа, и если оно есть - проверяет фильтром. Все подходящие элементы - возвращаются также в IEnumerable.

Далее этот метод можно использовать следующим образом. Например, если нам нужно отфильтровать все объекты, у которых свойство MyEntity имеет ID 2 или 3, то пишем следующее:
C#
1
2
var allowedIDs = new HashSet<int> {2, 3};
var temp = list.Where<Entity>("MyEntity", e => allowedIDs.Contains(e.ID));
Если нужно проверить одновременно несколько условий, то пишем например так:
C#
1
2
var temp = list.Where<Entity>("MyEntity", e => e.ID == 2).
                Where<DateTime>("MyDate", e => e >= new DateTime(2017, 1, 1));
Полный код примера:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication228
{
    class Program
    {
        static void Main(string[] args)
        {
            //sample list
            var list = new List<Object>()
            {
                new MyClass {MyEntity = new Entity {ID = 1}, MyDate = DateTime.Now},
                new MyClass {MyEntity = new Entity {ID = 2}, MyDate = DateTime.MinValue},
                new MyClass {MyEntity = new Entity {ID = 3}, MyDate = DateTime.Now}
            };
 
            //filter objects where MyEntity.ID = 2 or 3
            var allowedIDs = new HashSet<int> {2, 3};
            var temp = list.Where<Entity>("MyEntity", e => allowedIDs.Contains(e.ID));
            Show(temp);//show list
 
            //filter where Date > 2017 year
            temp = temp.Where<DateTime>("MyDate", e => e >= new DateTime(2017, 1, 1));
            Show(temp);//show list
 
            Console.ReadLine();
        }
 
        private static void Show(IEnumerable list)
        {
            Console.WriteLine("-----------");
            foreach (var item in list)
                Console.WriteLine(item);
        }
    }
 
    public static class FilterHelper
    {
        public static IEnumerable Where<T>(this IEnumerable list, string propertyName, Func<T, bool> filter)
        {
            foreach(var obj in list)
            if (obj != null)
            {
                var pi = obj.GetType().GetProperty(propertyName);
                if (pi != null && pi.PropertyType == typeof(T))
                if (filter((T)pi.GetValue(obj)))
                    yield return obj;
            }
        }
    }
 
    public class Entity
    {
        public virtual int ID { get; set; }
        public virtual string Description { get; set; }
    }
 
    public class MyClass
    {
        public Entity MyEntity { get; set; }
        public DateTime MyDate { get; set; }
 
        public override string ToString()
        {
            return string.Format("MyEntity: {0} MyDate: {1}", MyEntity.ID, MyDate);
        }
    }
}
2
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
04.04.2018, 15:27  [ТС]
Storm23, спасибо за Ваш комментарий! С точки зрения, того, что этот путь проще, чем с Expression Trees, спору нет.
А как можно доработать Ваш код, с учетом следующих пожеланий:
1. В качестве параметра метода передавать только коллекцию имен свойств и коллекцию коллекций допустимых ID (List<List<int>>);
2. Убрать лямбда-выражение (e => allowedIDs.Contains(e.ID)) внутрь метода. (все фильтруемые свойства являются наследниками базового класса со свойствами string Description и int Id).

Добавлено через 4 минуты
Т.е. я хочу написать что-то вроде

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        public static IEnumerable Where<T>(this List<T> list, List<string> propertyName, List<int> allowedID) where T:Entity
        {
            for (int i = 0; i < list.Count; i++)
            {
                if (list[i] != null)
                for (int u = 0; u < propertyName.Count; u++)
                {
                    var pi = list[i].GetType().GetProperty(propertyName[u]);
                    if (pi != null && pi.PropertyType == typeof(T))
                        if (filter((T)pi.GetValue(list[i])))
                            yield return list[i];
                }
            }
}
Но не понимаю, что должно быть вместо filter.
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
04.04.2018, 15:46
Лучший ответ Сообщение было отмечено wrq2017 как решение

Решение

Цитата Сообщение от wrq2017 Посмотреть сообщение
1. В качестве параметра метода передавать только коллекцию имен свойств и коллекцию коллекций допустимых ID (List<List<int>>);
2. Убрать лямбда-выражение (e => allowedIDs.Contains(e.ID)) внутрь метода. (все фильтруемые свойства являются наследниками базового класса со свойствами string Description и int Id).
Если нужно отобрать по ИЛИ (то есть те объекты, которые содержат хотя бы одно свойство с нужным ID):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public static class FilterHelper
    {
        ....
 
        public static IEnumerable Where<T>(this IEnumerable list, List<string> propertyNames, List<List<int>> IDs)
        {
            var res = new HashSet<object>();
 
            for (int i=0; i < propertyNames.Count; i++)
            foreach (var obj in list.Where<Entity>(propertyNames[i], e => IDs[i].Contains(e.ID)))
                res.Add(obj);
 
            return res;
        }
    }
2
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
05.04.2018, 10:12  [ТС]
Storm23, вопрос по последнему сообщению. Вызов метода Where получается рекурсивный? Тогда не соответствуют аргументы метода.

Добавлено через 27 секунд
У Вас этот код ошибок не выдаёт?
0
 Аватар для Fleder
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
05.04.2018, 11:07
Цитата Сообщение от wrq2017 Посмотреть сообщение
У Вас этот код ошибок не выдаёт?
Просто товарищ Storm23 для краткости привёл не весь код хелпера.
Полный будет такой:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   public static class FilterHelper
   {
      public static IEnumerable Where<T>(this IEnumerable list, string propertyName, Func<T, bool> filter)
      {
         foreach(var obj in list)
            if(obj != null)
            {
               var pi = obj.GetType().GetProperty(propertyName);
               if(pi != null && pi.PropertyType == typeof(T))
                  if(filter((T)pi.GetValue(obj)))
                     yield return obj;
            }
      }
 
      public static IEnumerable Where<T>(this IEnumerable list, List<string> propertyNames, List<List<int>> IDs)
      {
         var res = new HashSet<object>();
 
         for(int i = 0; i < propertyNames.Count; i++)
            foreach(var obj in list.Where<Entity>(propertyNames[i], e => IDs[i].Contains(e.ID)))
               res.Add(obj);
 
         return res;
      }
   }
2
3 / 3 / 2
Регистрация: 30.07.2017
Сообщений: 223
05.04.2018, 14:38  [ТС]
Fleder, понятно, спасибо!

Добавлено через 3 часа 29 минут
Fleder, Storm23, уважаемые коллеги, огромное спасибо за Вашу помощь и, собственно, всем высказавшимся по теме!!!
Боюсь показаться назойливым, но всё-таки есть еще вопрос.
Допустим модель данных выглядит так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    public class BaseDictionary
    {
        public int ID { get; set; }
        public string Description { get; set; }
    }
 
    public class A : BaseDictionary
    {
 
    }
    public class B : BaseDictionary
    {
 
    }
 
    public class C : BaseDictionary
    {
 
    }
 
    public class SomeMainClass
    {
        public A A { get; set; }
        public B B { get; set; }
        public C C { get; set; }
        public override string ToString()
        {
            return string.Format("A: {0} B: {1} C: {2}", A.ID, B.ID, C.ID);
        }
    }
Тогда первый предложенный метод Where будет работать следующим образом (по моему представлению):
C#
1
2
3
4
5
6
7
8
9
            
var list = new List<Object>()
            {
                new SomeMainClass {A = new A {ID = 1}, B = new B {ID = 2}, C = new C {ID = 3}},
                new SomeMainClass {A = new A {ID = 11}, B = new B {ID = 22}, C = new C {ID = 33 }},
                new SomeMainClass {A = new A {ID = 111}, B = new B {ID = 222}, C = new C {ID = 333 }}
            };
            var tempMain = list.Where<A>("A", x => x.ID == 1);
            tempMain = tempMain.Where<B>("B", x => x.ID == 2);
Второй метод Where вообще не применим, т.к. у одного класса-свойства только одно ключевое свойство, по которому будет осуществляться фильтрация (только ID), а не несколько.
Иначе говоря, я предполагал, что второй метод (как изначально поставленный вопрос) будет работать вот так:
C#
1
2
3
4
5
6
            List<int> listAid = new List<int>() { 1 };
            List<int> listBid = new List<int>() { 2 };
            List<List<int>> allList= new List<List<int>>();
            allList.Add(listAid);
            allList.Add(listBid);
           var tempMain = list.Where<SomeMainClass>(new List<string>() { "A", "B" }, allList);
Но по понятным причинам он не работает. Что нужно сделать, чтобы привести к этому виду?
0
 Аватар для Fleder
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
05.04.2018, 18:22
Цитата Сообщение от wrq2017 Посмотреть сообщение
Что нужно сделать, чтобы привести к этому виду?
Можно применить такую конструкцию:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
      public static void Main(string[] args)
      {
         var list = new List<Object>()
         {
             new SomeMainClass {A = new A {ID = 1}, B = new B {ID = 2}, C = new C {ID = 3}},
             new SomeMainClass {A = new A {ID = 11}, B = new B {ID = 22}, C = new C {ID = 33 }},
             new SomeMainClass {A = new A {ID = 111}, B = new B {ID = 222}, C = new C {ID = 333 }}
         };
 
         List<int> listAid = new List<int>() { 1 };
         List<int> listBid = new List<int>() { 2 };
         List<List<int>> allList = new List<List<int>>();
         allList.Add(listAid);
         allList.Add(listBid);
 
         var tempList = list
            .Where<SomeMainClass, BaseDictionary>(new[] { "A", "B" }, allList)
            .Cast<object>()
            .ToList();
 
         Console.ReadKey();
      }
   }
 
   public static class FilterHelper
   {
      public static IEnumerable Where<T, B>(this IEnumerable list, IEnumerable<string> propertyNames, List<List<int>> allowedID)
         where T : SomeMainClass
         where B : BaseDictionary
      {
         foreach(SomeMainClass some in list)
         {
            foreach(string propertyName in propertyNames)
            {
               var property = some.GetType().GetProperty(propertyName);
               if(property == null) yield break;
               foreach(List<int> internalList in allowedID)
               {
                  var x = (B)property.GetValue(some);
                  if(internalList.Contains(x.ID))
                     yield return some;
               }
            }
         }
      }
   }
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
05.04.2018, 18:43
Цитата Сообщение от wrq2017 Посмотреть сообщение
Но по понятным причинам он не работает.
Проблема в том, что вы до сих пор не объяснили толком какова должна быть логика фильтра, и какие результаты вы ожидаете.
Вам причины понятны, а мне - нет.

Вероятно вы имеете ввиду что в обоих примерах должен отбираться первый объект? Тогда вот код. Он работает и для первого примера и для второго:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
 
namespace ConsoleApplication228
{
    class Program
    {
        static void Main(string[] args)
        {
            //sample 1
            var list = new List<Object>()
            {
                new SomeMainClass {A = new A {ID = 1}, B = new B {ID = 2}, C = new C {ID = 3}},
                new SomeMainClass {A = new A {ID = 11}, B = new B {ID = 22}, C = new C {ID = 33 }},
                new SomeMainClass {A = new A {ID = 111}, B = new B {ID = 222}, C = new C {ID = 333 }}
            };
            var tempMain = list.Where<A>("A", x => x.ID == 1);
            tempMain = tempMain.Where<B>("B", x => x.ID == 2);
 
            Show(tempMain);
 
            //sample 2
            List<int> listAid = new List<int>() { 1 };
            List<int> listBid = new List<int>() { 2 };
            List<List<int>> allList = new List<List<int>>();
            allList.Add(listAid);
            allList.Add(listBid);
            tempMain = list.Where(new List<string>() { "A", "B" }, allList);
 
            Show(tempMain);
 
 
            Console.ReadLine();
        }
 
        private static void Show(IEnumerable list)
        {
            Console.WriteLine("-----------");
            foreach (var item in list)
                Console.WriteLine(item);
        }
    }
 
    public static class FilterHelper
    {
        public static IEnumerable Where<T>(this IEnumerable list, string propertyName, Func<T, bool> filter)
        {
            foreach(var obj in list)
            if (obj != null)
            {
                var pi = obj.GetType().GetProperty(propertyName);
                if (pi != null)
                {
                    if(pi.PropertyType == typeof(T) || pi.PropertyType.IsSubclassOf(typeof(T)))
                    if (filter((T) pi.GetValue(obj)))
                        yield return obj;
                }
            }
        }
 
        public static IEnumerable Where(this IEnumerable list, List<string> propertyNames, List<List<int>> IDs)
        {
            var res = new HashSet<object>();
            for (int i=0; i < propertyNames.Count; i++)
            foreach (var obj in list.Where<BaseDictionary>(propertyNames[i], e => IDs[i].Contains(e.ID)))
                res.Add(obj);
 
            return res;
        }
    }
 
    public class BaseDictionary
    {
        public int ID { get; set; }
        public string Description { get; set; }
    }
 
    public class A : BaseDictionary
    {
 
    }
    public class B : BaseDictionary
    {
 
    }
 
    public class C : BaseDictionary
    {
 
    }
 
    public class SomeMainClass
    {
        public A A { get; set; }
        public B B { get; set; }
        public C C { get; set; }
        public override string ToString()
        {
            return string.Format("A: {0} B: {1} C: {2}", A.ID, B.ID, C.ID);
        }
    }
}


1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
05.04.2018, 18:43
Помогаю со студенческими работами здесь

Не могу сделать чтобы класс содержал основной конструктор и конструктор копирования
Разработать класс ThreeAngle для работы с плоскими треугольниками. В качестве членов-данных задаются длины трех сторон треугольника. Класс...

Конструктор класса, конструктор копирования запускается не тот который нужен
Есть такой конструктор: Neuron::Neuron(int iType_activation_funk) { this-&gt;iType_act = iType_activation_funk; switch...

this(Всегда ли вызывается конструктор при не явной передачи объекта в конструктор)
class Test { int i; public: void test(int i) { this -&gt; i = i; } }; Конструктор Test::Test всегда вызывается...

Будет ли определен компилятором конструктор по умолчанию, если есть конструктор с дефолтным параметром?
Добрый день, сразу к делу, есть некий класс class some { int x; public: some(int y = 1) : x(y) {} };

Создать класс. Написать конструктор по умолчанию, конструктор с параметрами. Перегрузить операции «меньше» и «равно»
Помогите пожалуйста, хоть с одним из заданий. 1. Создать класс. Написать конструктор по умолчанию, конструктор с параметрами....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru