Zoeken…


Opmerkingen

Expressiebomen zijn gegevensstructuren die worden gebruikt om code-expressies in het .NET Framework weer te geven. Ze kunnen worden gegenereerd door code en programmatisch worden doorlopen om de code naar een andere taal te vertalen of uit te voeren. De meest populaire generator van Expression Trees is de C # compiler zelf. De C # compiler kan expressiebomen genereren als een lambda-expressie wordt toegewezen aan een variabele van het type Expression <Func <... >>. Meestal gebeurt dit in de context van LINQ. De populairste consument is de LINQ-provider van Entity Framework. Het verbruikt de expressiebomen gegeven aan Entity Framework en genereert equivalente SQL-code die vervolgens wordt uitgevoerd tegen de database.

Eenvoudige expressieboom gegenereerd door de C # Compiler

Overweeg de volgende C # -code

Expression<Func<int, int>> expression = a => a + 1;

Omdat de C # compiler ziet dat de lambda-expressie is toegewezen aan een expressie-type in plaats van een gedelegeerde type, genereert het een expressieboom die ongeveer gelijk is aan deze code

ParameterExpression parameterA = Expression.Parameter(typeof(int), "a");
var expression = (Expression<Func<int, int>>)Expression.Lambda(
                                                 Expression.Add(
                                                     parameterA,
                                                     Expression.Constant(1)),
                                                 parameterA);

De wortel van de boom is de lambda-expressie die een body en een lijst met parameters bevat. De lambda heeft 1 parameter genaamd "a". De body is een enkele expressie van CLR type BinaryExpression en NodeType of Add. Deze uitdrukking staat voor toevoeging. Het heeft twee subexpressies die worden aangeduid als Links en Rechts. Links is de ParameterExpression voor de parameter "a" en Rechts is een ConstantExpression met de waarde 1.

Het eenvoudigste gebruik van deze uitdrukking is het afdrukken:

Console.WriteLine(expression); //prints a => (a + 1)

Hiermee wordt de equivalente C # -code afgedrukt.

De expressieboom kan worden gecompileerd in een C # -afgevaardigde en worden uitgevoerd door de CLR

Func<int, int> lambda = expression.Compile();
Console.WriteLine(lambda(2)); //prints 3

Gewoonlijk worden expressies vertaald naar andere talen zoals SQL, maar kunnen ook worden gebruikt om private, beschermde en interne leden van publieke of niet-publieke types aan te roepen als alternatief voor Reflection.

bouwen van een predikaat van formulierveld == waarde

Om een uitdrukking zoals _ => _.Field == "VALUE" op te bouwen tijdens runtime.

Gegeven een predikaat _ => _.Field en een tekenreekswaarde "VALUE" , maakt u een expressie die test of het predicaat waar is of niet.

De uitdrukking is geschikt voor:

  • IQueryable<T> , IEnumerable<T> om het predicaat te testen.
  • entiteitskader of Linq naar SQL om een Where clausule te maken die het predicaat test.

Met deze methode wordt een juiste uitdrukking Equal die test of Field gelijk is aan "VALUE" .

public static Expression<Func<T, bool>> BuildEqualPredicate<T>(
    Expression<Func<T, string>> memberAccessor,
    string term)
{
    var toString = Expression.Convert(Expression.Constant(term), typeof(string));
    Expression expression = Expression.Equal(memberAccessor.Body, toString);
    var predicate = Expression.Lambda<Func<T, bool>>(
        expression,
        memberAccessor.Parameters);
    return predicate;
}

Het predicaat kan worden gebruikt door het predicaat op te nemen in een Where uitbreidingsmethode.

var predicate = PredicateExtensions.BuildEqualPredicate<Entity>(
    _ => _.Field,
    "VALUE");
var results = context.Entity.Where(predicate).ToList();

Expressie voor het ophalen van een statisch veld

Met een voorbeeldtype zoals dit:

public TestClass
{
    public static string StaticPublicField = "StaticPublicFieldValue";
}

We kunnen de waarde van StaticPublicField ophalen:

var fieldExpr = Expression.Field(null, typeof(TestClass), "StaticPublicField");
var labmda = Expression.Lambda<Func<string>>(fieldExpr);

Het kan dan worden gecompileerd in een gedelegeerde voor het ophalen van de veldwaarde.

Func<string> retriever = lambda.Compile();
var fieldValue = retriever();

// fieldValue result is StaticPublicFieldValue

InvocationExpression Class

Met de klasse InvocationExpression kunnen andere lambda-expressies worden opgeroepen die deel uitmaken van dezelfde expressieboom.

U maakt ze met de statische Expression.Invoke methode.

Probleem We willen op de items komen die "auto" in hun beschrijving hebben. We moeten het op nul controleren voordat we naar een string zoeken, maar we willen niet dat het te overdreven wordt genoemd, omdat de berekening duur kan zijn.

using System;
using System.Linq;
using System.Linq.Expressions;
                    
public class Program
{
    public static void Main()
    {
        var elements = new[] {
            new Element { Description = "car" },
            new Element { Description = "cargo" },
            new Element { Description = "wheel" },
            new Element { Description = null },
            new Element { Description = "Madagascar" },
        };
    
        var elementIsInterestingExpression = CreateSearchPredicate(
            searchTerm: "car",
            whereToSearch: (Element e) => e.Description);
            
        Console.WriteLine(elementIsInterestingExpression.ToString());
            
        var elementIsInteresting = elementIsInterestingExpression.Compile();
        var interestingElements = elements.Where(elementIsInteresting);
        foreach (var e in interestingElements)
        {
            Console.WriteLine(e.Description);
        }
        
        var countExpensiveComputations = 0;
        Action incCount = () => countExpensiveComputations++;
        elements
            .Where(
                CreateSearchPredicate(
                    "car",
                    (Element e) => ExpensivelyComputed(
                        e, incCount
                    )
                ).Compile()
            )
            .Count();
                   
        Console.WriteLine("Property extractor is called {0} times.", countExpensiveComputations);
    }
    
    private class Element
    {
        public string Description { get; set; }
    }
    
    private static string ExpensivelyComputed(Element source, Action count)
    {
        count();
        return source.Description;
    }
    
    private static Expression<Func<T, bool>> CreateSearchPredicate<T>(
            string searchTerm,
            Expression<Func<T, string>> whereToSearch)
    {
        var extracted = Expression.Parameter(typeof(string), "extracted");
    
        Expression<Func<string, bool>> coalesceNullCheckWithSearch =
            Expression.Lambda<Func<string, bool>>(
                Expression.AndAlso(
                    Expression.Not(
                        Expression.Call(typeof(string), "IsNullOrEmpty", null, extracted)
                    ),
                    Expression.Call(extracted, "Contains", null, Expression.Constant(searchTerm))
                ),
                extracted);
                
        var elementParameter = Expression.Parameter(typeof(T), "element");
                
        return Expression.Lambda<Func<T, bool>>(
            Expression.Invoke(
                coalesceNullCheckWithSearch,
                Expression.Invoke(whereToSearch, elementParameter)
            ),
            elementParameter
        );
    }
}

uitgang

element => Invoke(extracted => (Not(IsNullOrEmpty(extracted)) AndAlso extracted.Contains("car")), Invoke(e => e.Description, element))
car
cargo
Madagascar
Predicate is called 5 times.

Het eerste ding om op te merken is hoe de werkelijke propery-toegang, verpakt in een Invoke:

Invoke(e => e.Description, element)

, en dit is het enige onderdeel dat e.Description raakt, en in plaats daarvan wordt de extracted parameter van het type string doorgegeven aan het volgende:

(Not(IsNullOrEmpty(extracted)) AndAlso extracted.Contains("car"))

Een ander belangrijk ding om op te merken is AndAlso . Het berekent alleen het linker deel, als het eerste deel 'onwaar' retourneert. Het is een veelgemaakte fout om de bitsgewijze operator 'And' te gebruiken in plaats van deze, die altijd beide delen berekent en in dit voorbeeld zou mislukken met een NullReferenceException.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow