Ahogy a LINQ bevezetőben szó volt róla, a LINQ használható SQL rendszerek esetén is a megfelelő kapcsolódási felülettel, de mégis hogy lesz C# kódból SQL kód, amit majd az adatbázis végrehajt? Emögött az Expression1 osztály és az IQueryable 2 felület áll. A LINQ szolgáltatóknak implementálniuk kell ezt a felületet.
Ez a felület egy LINQ kérésbÅ‘l készült kifejezés fát kap meg, ami alapján a felület implementálója valamilyen módon (pl. SQL utasÃtássorra fordÃtás után) .NET objektumokat tud visszaadni.
A kifejezésfa egy faszerű adatstruktúra, ahol minden csomópont egy kifejezés. Ez a kifejezés lehet egy metódushÃvás vagy egy bináris (két operandusú) művelet is. Használatuk lehetÅ‘vé teszi a kód dinamikus módosÃtását is. Erre leginkább a keretrendszer DLR része épÃt.
Ezeket a fákat vizuálisan könnyebb elképzelni, ezért nézzük meg egy egyszerű kifejezés fáját:
int x = 3 + 2;
A faszerű kifejezéssé alakÃtás elÅ‘nye, hogy fa bejáró algoritmusokkal egyszerűsÃthetÅ‘ a kifejezés, illetve ki is értékelhetÅ‘. Szinte minden modern fordÃtóprogram a beadott szöveges kódot valami gráfszerű belsÅ‘ reprezentációvá alakÃtja fordÃtás elÅ‘tt.
LINQ esetén a megÃrt kódunkból kifejezésfát a fordÃtó fog épÃteni és az IQueryable implementációnk hÃváskor már a felépÃtett fát fogja megkapni egy Expression tÃpusú objektum formájában. Az alábbi program az Expression osztály használatát mutatja be:
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace PeldaExpressionTree
{
public static class Program
{
public static void Main(string[] args)
{
//Lamba stÃlusú kifejezésfa épÃtés
Expression<Func<int, int, int>> osszead = (a, b) => a + b;
//Kifejezésfa fordÃtása
Func<int, int, int> osszeadLambda = osszead.Compile();
//MeghÃvása
int eredmeny = osszeadLambda(1, 2);
Console.WriteLine("1 + 2 = {0}", eredmeny);
//Komplex példa
var HelloLambda = Hello();
Console.WriteLine("Hello(\"Olvaso\") = {0}", HelloLambda("Olvaso"));
Console.ReadKey();
}
private static Func<string, string> Hello()
{
ParameterExpression neve = Expression.Parameter(typeof(string), nameof(neve));
MethodInfo IsNullOrEmptyMetodus =
typeof(string)
.GetMethod(nameof(string.IsNullOrWhiteSpace));
UnaryExpression feltetel
= Expression.Not(Expression.Call(IsNullOrEmptyMetodus, neve));
var concatMetodus =
typeof(string).GetMethod(nameof(string.Concat),
new[]
{
typeof(string),
typeof(string)
});
var igazEset =
Expression.Call(concatMetodus, Expression.Constant("Hello, "), neve);
ConstantExpression hamisEset = Expression.Constant(null, typeof(string));
var lambda =
Expression.Lambda<Func<string, string>>(
Expression.Condition(feltetel, igazEset, hamisEset),
neve);
return lambda.Compile();
}
}
}
A program kimenete:
1 + 2 = 3
Hello("Olvaso") = Hello, Olvaso
Amint látható ,egy kifejezés fából tudunk egy lambda metódust csinálni a Compile() metódus meghÃvásával, amit utána végre tudunk hajtani, illetve ahogy látható, épÃtÅ‘kövenlként tudunk kifejezéseket is generálni. Ez a HelloLambda létrehozásánál látható. Ennek a C# ekvivalens kódja:
string Hello(string neve)
{
return !string.IsNullOrWhiteSpace(neve)) ? $"Hello, {neve}" : null;
}
A létrehozott Expression osztály immutable, hasonlóan a string osztályhoz, vagyis minden módosÃtás egy új kifejezést eredményez. A kifejezésekbÅ‘l felépÃtett kód nem a legegyszerűbben olvasható. Ennek segÃtségével lehet dinamikusan kódot generálni és fordÃtani az alkalmazásunkban, de a bonyolultsága miatt nemigen kézenfekvÅ‘. Erre jó példa a szöveg összefűzése. Ehhez a string tÃpuson ki kell keresnünk a megfelelÅ‘ szignatúrával rendelkezÅ‘ Concat metódust, majd paraméterezetten meghÃvnunk. Mindezt C# kód esetén a fordÃtó a + operátor használatakor automatikusan megteszi számunkra. Ezért ha C# kódot szeretnénk generálni, akkor sokkal kézenfekvÅ‘bb a Roslyn API használata, mint ahogy azt a DLR esetén is láttuk a C# példában.
Ha véletlenül Expression osztályokkal kellene dolgoznunk a kódban, akkor mindenképpen hasznos kiegészÃtÅ‘ az Expression Tree Visualizer, ami kifejezésfák vizualizálását teszi lehetÅ‘vé Visual Studio debuggolás közben. A kiegészÃtÅ‘ a https://github.com/zspitz/ExpressionTreeVisualizer cÃmrÅ‘l szerezhetÅ‘ be.
Összegezve tehát az Expression osztály leginkább LINQ provider-ek implementálásra lett kitalálva, ahol C# kódot kell átalakÃtanunk valami más nyelvre. Ehhez pedig egy C# kifejezésrÅ‘l tudnunk kell, hogy milyen elemekbÅ‘l épült fel.