Изменить тип возврата из списка & lt; Func & lt; T, bool & gt; & gt; & gt; к одному Func & lt; T, bool & gt; [Дубликат]

Теперь вы можете добавлять / использовать экземпляры с помощью метода

public class Myprogram7 {

  Scanner scan;
  int compareCount = 0;
  int low = 0;
  int high = 0;
  int mid = 0;  
  int key = 0;  
  Scanner temp;  
  int[]list;  
  String menu, outputString;  
  int option = 1;  
  boolean found = false;  

  private void readLine() {

  }

  private void findkey() {

  }

  private void printCount() {

  }
  public static void main(String[] args){

    Myprogram7 myprg=new Myprogram7();
    myprg.readLine();
    myprg.findkey();
    myprg.printCount();
  }
}
186
задан andy 27 June 2009 в 02:04
поделиться

7 ответов

Ну, вы можете использовать Expression.AndAlso / OrElse и т. д. для объединения логических выражений, но проблема заключается в параметрах; вы работаете с тем же ParameterExpression в expr1 и expr2? Если это так, это проще:

var body = Expression.AndAlso(expr1.Body, expr2.Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);

Это также хорошо работает, чтобы свести на нет одну операцию:

static Expression<Func<T, bool>> Not<T>(
    this Expression<Func<T, bool>> expr)
{
    return Expression.Lambda<Func<T, bool>>(
        Expression.Not(expr.Body), expr.Parameters[0]);
}

В противном случае, в зависимости от поставщика LINQ, вы можете скомбинируйте их с Invoke:

// OrElse is very similar...
static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> left,
    Expression<Func<T, bool>> right)
{
    var param = Expression.Parameter(typeof(T), "x");
    var body = Expression.AndAlso(
            Expression.Invoke(left, param),
            Expression.Invoke(right, param)
        );
    var lambda = Expression.Lambda<Func<T, bool>>(body, param);
    return lambda;
}

Где-то у меня есть код, который переписывает дерево выражений, заменяя узлы, чтобы удалить необходимость в Invoke, но он довольно длинный (и Я не могу вспомнить, где я его оставил ...)


Обобщенная версия, которая выбирает самый простой маршрут:

static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> expr1,
    Expression<Func<T, bool>> expr2)
{
    // need to detect whether they use the same
    // parameter instance; if not, they need fixing
    ParameterExpression param = expr1.Parameters[0];
    if (ReferenceEquals(param, expr2.Parameters[0]))
    {
        // simple version
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(expr1.Body, expr2.Body), param);
    }
    // otherwise, keep expr1 "as is" and invoke expr2
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(
            expr1.Body,
            Expression.Invoke(expr2, param)), param);
}

Начиная с .net 4.0. Существует класс ExpressionVistor, который позволяет создавать выражения, безопасные для EF.

    public static Expression<Func<T, bool>> AndAlso<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var parameter = Expression.Parameter(typeof (T));

        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
        var left = leftVisitor.Visit(expr1.Body);

        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
        var right = rightVisitor.Visit(expr2.Body);

        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(left, right), parameter);
    }



    private class ReplaceExpressionVisitor
        : ExpressionVisitor
    {
        private readonly Expression _oldValue;
        private readonly Expression _newValue;

        public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
        {
            _oldValue = oldValue;
            _newValue = newValue;
        }

        public override Expression Visit(Expression node)
        {
            if (node == _oldValue)
                return _newValue;
            return base.Visit(node);
        }
    }
236
ответ дан Marc Gravell 28 August 2018 в 01:49
поделиться
  • 1
    Эй, Марк, я попробовал ваше первое предложение в первом блоке кода выше, но когда я перехожу в «лямбда», Выражение & л; & FUNC л; Т, BOOL & GT; & GT; результат в методе Where, я получаю ошибку, говоря, что параметр выходит за рамки? Есть идеи? ура – andy 27 June 2009 в 06:52
  • 2
    +1 обобщенная версия работает как шарм, я использовал And вместо andsoso, я думал, что linq для sql не поддерживает andsoso? – Maslow 4 September 2009 в 14:57
  • 3
    @Maslow - вот переписывающий, который может встроить деревья для сохранения Invoke: stackoverflow.com/questions/1717444/… – Marc Gravell♦ 19 March 2011 в 22:12
  • 4
    @Aron теперь смотрит на дату: посетитель платформы .NET (ExpressionVisitor) не существовал ; У меня есть связанный пример с stackoverflow из той же даты, когда он вручную запускает посетителя: это lot кода. – Marc Gravell♦ 13 August 2014 в 08:09
  • 5
    @MarkGravell, я использую ваше первое решение для комбинирования моих выражений, и все работает нормально даже в сущностной структуре. Итак, каковы были бы преимущества использования последнего решения? – johnny 5 3 August 2017 в 00:08

Вы можете использовать Expression.AndAlso / OrElse для объединения логических выражений, но вы должны убедиться, что значения параметра одинаковы.

У меня были проблемы с EF и PredicateBuilder , поэтому я сделал свой собственный, не прибегая к Invoke, чтобы я мог использовать вот так:

var filterC = filterA.And(filterb);

Исходный код для моего PredicateBuilder:

public static class PredicateBuilder {

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }   
}

И класс утилиты для замены параметры в лямбда:

internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
        public Dictionary<Expression, Expression> subst = new Dictionary<Expression, Expression>();

        protected override Expression VisitParameter(ParameterExpression node) {
            Expression newValue;
            if (subst.TryGetValue(node, out newValue)) {
                return newValue;
            }
            return node;
        }
    }
42
ответ дан Adam Tegen 28 August 2018 в 01:49
поделиться
  • 1
    Это решение было единственным, что позволило мне иметь x = & gt; x.Property == Значение, объединенное с arg = & gt; arg.Property2 == Значение. Основные реквизиты, немного краткие и запутывающие, но это работает, поэтому я не собираюсь жаловаться. Kudos Adam :-) – VulgarBinary 12 December 2012 в 22:38
  • 2
    Благодарю. Я нашел код полезным во многих местах. – Adam Tegen 13 December 2012 в 21:10
  • 3
    Это отличное решение. – Aaron Stainback 14 May 2014 в 00:26
  • 4
    Адам, это решило очень неприятную проблему, с которой я столкнулся с Linq-провайдером модели SharePoint Client Object - спасибо за его публикацию. – Christopher McAtackney 9 July 2014 в 15:30
  • 5
    Это сработало для меня! Я искал множество решений, а также создатель предикатов, и до этого ничего не работало. Спасибо! – tokyo0709 1 August 2016 в 20:10

Я думаю, что это прекрасно работает, не так ли?

Func<T, bool> expr1 = (x => x.Att1 == "a");
Func<T, bool> expr2 = (x => x.Att2 == "b");
Func<T, bool> expr1ANDexpr2 = (x => expr1(x) && expr2(x));
Func<T, bool> expr1ORexpr2 = (x => expr1(x) || expr2(x));
Func<T, bool> NOTexpr1 = (x => !expr1(x));
-6
ответ дан Céline 28 August 2018 в 01:49
поделиться
  • 1
    это не может использоваться в Linq для SQL, например – Romain Vergnory 12 May 2016 в 09:47

Джо Альбахари (автор C # 3.0 в двух словах и LINQPad) написал утилиту под названием PredicateBuilder, которая может использоваться для функций AND и OR.

http: //www.albahari. com / nutshell / predicatebuilder.aspx

Хотя он работает с функциями, он является открытым исходным кодом, поэтому вы можете проверить его и посмотреть, как он работает.

27
ответ дан Cameron MacFarland 28 August 2018 в 01:49
поделиться
  • 1
    Лучшее решение! Выбранный ответ не так ясен, как решение PredicateBuilder. +1 за это! – Eduardo Xavier 17 October 2011 в 17:51
  • 2
    @Eduardo: Я не согласен. Код PredicateBuilder во многом идентичен выбранному ответу, но выбранный ответ предоставляет объяснение / код напрямую, а не через ссылку. – Brian 30 March 2012 в 16:37
  • 3
    PredicateBuilder НЕ РАБОТАЕТ В EF !!!!!!!!!!!!!!! Он оценивает запрос с сервера, затем применяет требуемый предикат в памяти вместо того, чтобы передавать предикат SQL, чтобы ограничить набор результатов. Вот почему PredicateBuilder нежелателен для этого варианта использования! BUYER BEWARE – VulgarBinary 12 December 2012 в 22:40
  • 4
    @VulgarBinary Возможно, вы могли бы потратить некоторое время на выяснение, почему он не работает в EF5, а не ждет ответа на ложь. – Cameron MacFarland 13 December 2012 в 02:39
  • 5
    @CameronMacFarland Я уже написал новый PredicateBuilder, который работает и оптимизирует деревья выражений. Этот фрагмент дал мне последнюю часть, в которой я нуждался, которая была компонентом посетителя. Я почти не ждал, когда что-нибудь подпишут ложкой, если вы серьезно хотите, чтобы ваши очки вернулись, что плохо отредактировал ваш ответ, и я верну -1. Однако, когда кто-то ищет что-то делать с Expression & lt; Func & lt; T, bool & gt; & gt; & gt; обычно это означает, что они хотят использовать EF. В противном случае они просто использовали бы Func & lt; T, bool & gt; – VulgarBinary 13 December 2012 в 03:01

Если поставщик не поддерживает Invoke, и вам нужно объединить два выражения, вы можете использовать ExpressionVisitor для замены параметра во втором выражении параметром в первом выражении.

class ParameterUpdateVisitor : ExpressionVisitor
{
    private ParameterExpression _oldParameter;
    private ParameterExpression _newParameter;

    public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
    {
        _oldParameter = oldParameter;
        _newParameter = newParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (object.ReferenceEquals(node, _oldParameter))
            return _newParameter;

        return base.VisitParameter(node);
    }
}

static Expression<Func<T, bool>> UpdateParameter<T>(
    Expression<Func<T, bool>> expr,
    ParameterExpression newParameter)
{
    var visitor = new ParameterUpdateVisitor(expr.Parameters[0], newParameter);
    var body = visitor.Visit(expr.Body);

    return Expression.Lambda<Func<T, bool>>(body, newParameter);
}

[TestMethod]
public void ExpressionText()
{
    string text = "test";

    Expression<Func<Coco, bool>> expr1 = p => p.Item1.Contains(text);
    Expression<Func<Coco, bool>> expr2 = q => q.Item2.Contains(text);
    Expression<Func<Coco, bool>> expr3 = UpdateParameter(expr2, expr1.Parameters[0]);

    var expr4 = Expression.Lambda<Func<Recording, bool>>(
        Expression.OrElse(expr1.Body, expr3.Body), expr1.Parameters[0]);

    var func = expr4.Compile();

    Assert.IsTrue(func(new Coco { Item1 = "caca", Item2 = "test pipi" }));
}
16
ответ дан Francis 28 August 2018 в 01:49
поделиться
  • 1
    Это решило мою конкретную проблему, когда другое решение привело к тому же исключению. Благодарю. – Shaun Wilson 8 March 2013 в 01:49
  • 2
    Это отличное решение. – Aaron Stainback 14 May 2014 в 00:25

Я предлагаю еще одно усовершенствование решений PredicateBuilder и ExpressionVisitor. Я назвал его UnifyParametersByName, и вы можете найти его в моей MIT-библиотеке: LinqExprHelper . Это позволяет комбинировать произвольные лямбда-выражения. Обычно задаются вопросы о предикатном выражении, но эта идея распространяется и на проекционные выражения.

В следующем коде используется метод ExprAdres, который создает сложное параметризованное выражение, используя встроенную lambda. Это сложное выражение кодируется только один раз, а затем повторно используется благодаря мини-библиотеке LinqExprHelper.

public IQueryable<UbezpExt> UbezpFull
{
    get
    {
        System.Linq.Expressions.Expression<
            Func<UBEZPIECZONY, UBEZP_ADRES, UBEZP_ADRES, UbezpExt>> expr =
            (u, parAdrM, parAdrZ) => new UbezpExt
            {
                Ub = u,
                AdrM = parAdrM,
                AdrZ = parAdrZ,
            };

        // From here an expression builder ExprAdres is called.
        var expr2 = expr
            .ReplacePar("parAdrM", ExprAdres("M").Body)
            .ReplacePar("parAdrZ", ExprAdres("Z").Body);
        return UBEZPIECZONY.Select((Expression<Func<UBEZPIECZONY, UbezpExt>>)expr2);
    }
}

И это строительный код подвыражения:

public static Expression<Func<UBEZPIECZONY, UBEZP_ADRES>> ExprAdres(string sTyp)
{
    return u => u.UBEZP_ADRES.Where(a => a.TYP_ADRESU == sTyp)
        .OrderByDescending(a => a.DATAOD).FirstOrDefault();
}

То, что я пытался достичь, - это выполнить параметризованные запросы без необходимости скопировать-вставить и с возможностью использования встроенных lambdas, которые так хороши. Без всех этих вспомогательных выражений я был бы вынужден создать целый запрос за один раз.

0
ответ дан Jarekczek 28 August 2018 в 01:49
поделиться

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

    public static LambdaExpression CombineOr(Type sourceType, LambdaExpression exp, LambdaExpression newExp) 
    {
        var parameter = Expression.Parameter(sourceType);

        var leftVisitor = new ReplaceExpressionVisitor(exp.Parameters[0], parameter);
        var left = leftVisitor.Visit(exp.Body);

        var rightVisitor = new ReplaceExpressionVisitor(newExp.Parameters[0], parameter);
        var right = rightVisitor.Visit(newExp.Body);

        var delegateType = typeof(Func<,>).MakeGenericType(sourceType, typeof(bool));
        return Expression.Lambda(delegateType, Expression.Or(left, right), parameter);
    }
0
ответ дан VorTechS 28 August 2018 в 01:49
поделиться