Как создать Predicate<T> динамически во время выполнения

Я пытаюсь создать метод в базовом классе, способный принимать параметры и создавать Predicate<T> на лету.

Вот абстрактный класс:

public abstract class Table<TResults>
    where TResults : class
{
    ...
    protected abstract List<TResults> Results { get; set; }
    ...
}

А вот один класс, реализующий Table<TResults>:

public class TrStudent
{
    ...

    public string Name => // some code
    ...

    public void Check()
    {
        // check implementation
    }
}

public class TableStudents : Table<TrStudent>
{
    ...
    protected override List<TrStudent> Results { get; set; }
    ...
    
    public void Check_Student(string studentName) => Results.Find(r => r.Name == studentName).Check();        
}

А вот еще один класс, реализующий Table<TResults>:

public class TrAnswer
{
    ...
    public string Name => // some code
    public int Id => // some other code
    ...
    
    public void Report()
    {
        // report implementation
    }
}

public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
    ...
    protected override List<TrAnswer> Results { get; set; }
    ...
    
    public void Report_Answer(string answerName, int answerId) => Results.Find(r => r.Name == answerName && r.Id == answerId).Report();
    ...
}

Что я хотел бы сделать, если это возможно, так это обновить класс Table<TResults> до:

public abstract class Table<TResults>
    where TResults : class
{
    ...
    protected abstract List<TResults> Results { get; set; }
    ...
    protected abstract Predicate<T> Predicate { get; }
    protected T Find(parameters) => Results.Find(parameters, Predicate);
}

Поэтому я могу обновить производные классы до:

public class TableStudents : Table<TrStudent>
{
    ...        
    public void Check_Student(string studentName) => Find(studentName).Check();
}

public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
    ...       
    public void Report_Answer(string answerName, int answerId) => Find(answerName, answerId).Report();
}

Но я не уверен, возможно ли это, так как некоторые лямбды принимают больше параметров, чем другие.

Я проверил Предикат , Лямбда-выражения , а также Класс выражений и почти уверен, что это можно сделать, но не знаю, с чего начать.

Спасибо за ваше время.

Отвечает ли это на ваш вопрос? Динамически генерировать предикат во время выполнения

Dave 15.12.2020 19:23

Привет, Дэйв, хорошо, это помогает, но я не уверен, как адаптировать его к тому, что я ищу. Спасибо.

elgato 15.12.2020 20:25
albahari.com/nutshell/predicatebuilder.aspx
Robert Harvey 15.12.2020 20:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
485
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Следующее не вариант?

public abstract class Table<TResults> where TResults : class {
    // ...
    protected TResults Find(Predicate<TResults> predicate)
        => Results.Find(predicate);
}
public class TableStudents : Table<TrStudent> {
    // ...
    public void Check_Student(string studentName)
        => Find(r => r.Name == studentName).Check();
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
    // ...
    public void Report_Answer(string answerName, int answerId)
        => Find(r => r.Name == answerName && r.Id == answerId).Report();
}

Я не вижу смысла в том, чтобы создавать Predicate<T> во время выполнения.


Вообще ваше решение выглядит слишком сложным, если честно. Какой смысл абстрагироваться List.Find(), когда каждый подкласс все равно должен переопределять List результатов?

Эти подклассы буквально имеют все необходимое для фильтрации своих результатов (то есть сами результаты и параметры предиката), но все же они должны просить абстрактный базовый класс отфильтровать их?


Если вам нужен предикат несколько раз, вы можете использовать приватную функцию в каждом подклассе, которая возвращает Predicate<TResults> для заданных параметров:

public class TableStudents : Table<TrStudent> {
    // ...

    public void Check_Student(string studentName)
        => Find(FilterBy(studentName)).Check();

    private static Predicate<TrStudent> FilterBy(string studentName)
        => r => r.Name == studentName;
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
    // ...
    public void Report_Answer(string answerName, int answerId)
        => Find(FilterBy(answerName, answerId)).Report();

    private static Predicate<TrAnswer> FilterBy(string answerName, int answerId)
        => r => r.Name == answerName && r.Id == answerId;
}

Не чувствуйте себя обязанным извлекать эти частные FilterBy() функции в базовый класс только потому, что они имеют идентичные имена и работают одинаково. То, как подкласс фильтрует, не относится к базовым классам. Подкласс лучше знает, как фильтровать свои результаты, и он может использовать или не использовать одну или несколько частных функций для создания необходимых ему Predicate<T>.


Обратите внимание, что FilterBy() — это функция, которая возвращает Predicate<T>. Predicate<T> — это функциональный объект, который возвращает bool, когда вы присваиваете ему значение T.

Это похоже на обычную функцию, такую ​​как bool MyPredicate(T value) {...}, только вы можете хранить ее в переменных, передавать и даже возвращать из других функций:

// create function object
var isAlphaNumeric = new Predicate<char>(c => char.IsLetter(c) || char.IsDigit(c));

// call function object with some values
Debug.Assert(isAlphaNumeric('a') == true);
Debug.Assert(isAlphaNumeric('&') == false);

Эта более подробная версия FilterBy() может прояснить связь с isAlphaNumeric:

private static Predicate<TrStudent> FilterBy(string studentName) {
    var hasName = new Predicate<TrStudent>(r => r.Name == studentName);
    return hasName;
}

Основное различие между isAlphaNumeric и hasName заключается в том, что hasName необходимо захватить значение параметра studentName, сохранив его внутри объекта функции. Позже, когда возвращаемый объект функции hasName вызывается List.Filter() с одним или несколькими объектами TrStudent, это значение будет доступно для сравнения имен.


Кстати, функции, возвращающие функцию (или принимающие в качестве параметров другие функции) — это так называемые функции высшего порядка. C# взял их из функционального программирования, и они очень мощные. Например, LINQ был бы невозможен без них. Но они также могут заменить некоторые шаблоны объектно-ориентированного проектирования, такие как шаблон стратегии, шаблон метода шаблона, фабричные шаблоны и даже внедрение зависимостей.

Привет, спокойной ночи, Nerd Pride, проблема в том, что каждый Table<TResults> имеет множество методов, которые снова и снова используют один и тот же Predicate<T>, поэтому я хотел сохранить его СУХИМ. Когда я начал пытаться решить эту проблему, я думал, что это будет намного проще, чем оказалось. Однако сейчас я просто хотел научиться это делать. Спасибо.

elgato 15.12.2020 20:59

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

Good Night Nerd Pride 15.12.2020 21:08

если бы я мог спросить вас, как private static Predicate<TrAnswer> FilterBy(string answerName, int answerId) => r => r.Name == answerName && r.Id == answerId; имеет доступ к «r»? Я не понимаю магии этой части. Спасибо!

elgato 18.12.2020 15:00

Другие вопросы по теме