SlideShare a Scribd company logo
Oleksii Holub "Expression trees in C#"
Speaker: Alexey Golub @Tyrrrz
Expression Trees in C#
I heard you like code, so we put code in your code so you can code while you code
/whois ${speaker}
Speaker: Alexey Golub @Tyrrrz
• Open-source developer ✨
• Conference speaker & blogger 🌐️
• C#, F#, JavaScript 💻
• Cloud & web ☁️
• Automation & DevOps ⚙️
What is an expression tree?
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
+Constant (2) Constant (3)
Plus operator
2 3
Binary expression
Speaker: Alexey Golub @Tyrrrz
!string.IsNullOrWhiteSpace(personName)
? "Greetings, " + personName
: null;
string? GetGreeting(string personName)
{
return
}
Speaker: Alexey Golub @Tyrrrz
"Greetings, "
!
personName
string.IsNullOrWhiteSpace( )
null;:
? +
personName
OPERATOR "NOT" METHOD CALL
PARAMETER
PARAMETERCONSTANT OPERATOR "ADD"
CONSTANT
Speaker: Alexey Golub @Tyrrrz
{ Ternary conditional }
{ + }
TRUE
{ null }
FALSE
{ Method call }
CONDITION
{ string.IsNullOrWhiteSpace }
{ personName }
{ personName }
{ "Greetings, " }
{ ! }
Speaker: Alexey Golub @Tyrrrz
Expression Tree
describes the structure of an expression
Expression Trees in .NET
• Object model represented by types deriving from
System.Linq.Expressions.Expression
• Can be constructed manually using factory methods
• Can be inferred from lambdas by the compiler
• Lambda expression trees can be converted into delegates
Speaker: Alexey Golub @Tyrrrz
Constructing Expression
Trees Manually
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Expression.Constant(...) ConstantExpression
Expression.New(...) NewExpression
Expression.Assign(...) BinaryExpression
Expression.Equal(...) BinaryExpression
Expression.Call(...) MethodCallExpression
Expression.Condition(...) ConditionalExpression
Expression.Loop(...) LoopExpression
...
Speaker: Alexey Golub @Tyrrrz
!string.IsNullOrWhiteSpace(personName)
? "Greetings, " + personName
: null;
Let’s recreate our expression dynamically
Speaker: Alexey Golub @Tyrrrz
public Func<string, string?> ConstructGreetingFunction()
{
var personNameParameter = Expression.Parameter(typeof(string), "personName");
var isNullOrWhiteSpaceMethod = typeof(string)
.GetMethod(nameof(string.IsNullOrWhiteSpace));
var condition = Expression.Not(
Expression.Call(isNullOrWhiteSpaceMethod, personNameParameter));
var trueClause = Expression.Add(
Expression.Constant("Greetings, "),
personNameParameter);
var falseClause = Expression.Constant(null, typeof(string));
var conditional = Expression.Condition(condition, trueClause, falseClause);
var lambda = Expression.Lambda<Func<string, string?>>(conditional, personNameParameter);
return lambda.Compile();
}
Speaker: Alexey Golub @Tyrrrz
var getGreeting = ConstructGreetingFunction();
var greetingForJohn = getGreeting("John");
The binary operator Add is not defined for the types
'System.String' and 'System.String'.
Speaker: Alexey Golub @Tyrrrz
We need to call string.Concat() directly
var concatMethod = typeof(string)
.GetMethod(nameof(string.Concat), new[] {typeof(string), typeof(string)});
var trueClause = Expression.Call(
concatMethod,
Expression.Constant("Greetings, "),
personNameParameter);
Speaker: Alexey Golub @Tyrrrz
var getGreetings = ConstructGreetingFunction();
var greetingsForJohn = getGreetings("John");
var greetingsForNobody = getGreetings(" ");
// "Greetings, John"
// <null>
Optimizing Reflection-Heavy
Code
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
How can we invoke Execute() from the outside?
public class Command
{
private int Execute() /> 42;
}
public static int CallExecute(Command command) =>
(int) typeof(Command)
.GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(command, null);
Speaker: Alexey Golub @Tyrrrz
public static class ReflectionCached
{
private static MethodInfo ExecuteMethod { get; } = typeof(Command)
.GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance);
public static int CallExecute(Command command) =>
(int) ExecuteMethod.Invoke(command, null);
}
public static class Re/lectionDelegate
{
private static MethodInfo ExecuteMethod { get; } = typeof(Command)
.GetMethod("Execute", Binding/lags.NonPublic | Binding/lags.Instance);
private static Func<Command, int> Impl { get; } =
(Func<Command, int>) Delegate
.CreateDelegate(typeof(Func<Command, int>), ExecuteMethod);
public static int CallExecute(Command command) /> Impl(command);
}
Using cached MethodInfo
Using Delegate.CreateDelegate
public static class ExpressionTrees
{
private static MethodInfo ExecuteMethod { get; } = typeof(Command)
.GetMethod("Execute", Binding/lags.NonPublic | Binding/lags.Instance);
private static Func<Command, int> Impl { get; }
static ExpressionTrees()
{
var instance = Expression.Parameter(typeof(Command));
var call = Expression.Call(instance, ExecuteMethod);
Impl = Expression.Lambda<Func<Command, int/>(call, instance).Compile();
}
public static int CallExecute(Command command) /> Impl(command);
}
Speaker: Alexey Golub @Tyrrrz
Lazy thread-safe
initialization via static
constructor
Speaker: Alexey Golub @Tyrrrz
public class Benchmarks
{
[Benchmark(Description = "Reflection", Baseline = true)]
public int Reflection() => (int) typeof(Command)
.GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(new Command(), null);
[Benchmark(Description = "Reflection (cached)")]
public int Cached() => ReflectionCached.CallExecute(new Command());
[Benchmark(Description = "Reflection (delegate)")]
public int Delegate() => ReflectionDelegate.CallExecute(new Command());
[Benchmark(Description = "Expressions")]
public int Expressions() => ExpressionTrees.CallExecute(new Command());
public static void Main() => BenchmarkRunner.Run<Benchmarks>();
}
| Method | Mean | Error | StdDev | Ratio |
|---------------------- |-----------:|----------:|----------:|------:|
| Reflection | 192.975 ns | 1.6802 ns | 1.4895 ns | 1.00 |
| Reflection (cached) | 123.762 ns | 1.1063 ns | 1.0349 ns | 0.64 |
| Reflection (delegate) | 6.419 ns | 0.0646 ns | 0.0605 ns | 0.03 |
| Expressions | 5.383 ns | 0.0433 ns | 0.0383 ns | 0.03 |
Implementing Generic
Operators
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
What can we do to support multiple types?
public int ThreeFourths(int x) => 3 * x / 4;
public int ThreeFourths(int x) => 3 * x / 4;
public long ThreeFourths(long x) => 3 * x / 4;
public float ThreeFourths(float x) => 3 * x / 4;
public double ThreeFourths(double x) => 3 * x / 4;
public decimal ThreeFourths(decimal x) => 3 * x / 4;
public T ThreeFourths<T>(T x) /> 3 * x / 4;
But we actually want something like this instead
Speaker: Alexey Golub @Tyrrrz
public T ThreeFourths<T>(T x)
{
var param = Expression.Parameter(typeof(T));
var three = Expression.Convert(Expression.Constant(3), typeof(T));
var four = Expression.Convert(Expression.Constant(4), typeof(T));
var operation = Expression.Divide(Expression.Multiply(param, three), four);
var lambda = Expression.Lambda<Func<T, T>>(operation, param);
var func = lambda.Compile();
return func(x);
}
var a = ThreeFourths(18); // 13
var b = ThreeFourths(6.66); // 4.995
var c = ThreeFourths(100M); // 75M
Speaker: Alexey Golub @Tyrrrz
public dynamic ThreeFourths(dynamic x) /> 3 * x / 4;
Wait, how is it different from this?
public static class ThreeFourths
{
private static class Impl<T>
{
public static Func<T, T> Of { get; }
static Impl()
{
var param = Expression.Parameter(typeof(T));
var three = Expression.Convert(Expression.Constant(3), typeof(T));
var four = Expression.Convert(Expression.Constant(4), typeof(T));
var operation = Expression.Divide(Expression.Multiply(param, three), four);
var lambda = Expression.Lambda<Func<T, T>>(operation, param);
Of = lambda.Compile();
}
}
public static T Of<T>(T x) => Impl<T>.Of(x);
}
Speaker: Alexey Golub @Tyrrrz
Generic, thread-safe
lazy initialization
Speaker: Alexey Golub @Tyrrrz
public class Benchmarks
{
[Benchmark(Description = "Static", Baseline = true)]
[Arguments(13.37)]
public double Static(double x) /> 3 * x / 4;
[Benchmark(Description = "Expressions")]
[Arguments(13.37)]
public double Expressions(double x) /> ThreeFourths.Of(x);
[Benchmark(Description = "Dynamic")]
[Arguments(13.37)]
public dynamic Dynamic(dynamic x) /> 3 * x / 4;
public static void Main() /> BenchmarkRunner.Run<Benchmarks>();
}
| Method | x | Mean | Error | StdDev | Ratio | RatioSD |
|------------ |------ |-----------:|----------:|----------:|------:|--------:|
| Static | 13.37 | 0.6077 ns | 0.0176 ns | 0.0147 ns | 1.00 | 0.00 |
| Expressions | 13.37 | 1.9510 ns | 0.0163 ns | 0.0145 ns | 3.21 | 0.08 |
| Dynamic | 13.37 | 19.3267 ns | 0.1512 ns | 0.1340 ns | 31.82 | 0.78 |
Parsing DSLs into Expression
Trees
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
public static class SimpleCalculator
{
private static readonly Parser<Expression> Constant =
Parse.DecimalInvariant
.Select(n => double.Parse(n, CultureInfo.InvariantCulture))
.Select(n => Expression.Constant(n, typeof(double)))
.Token();
private static readonly Parser<ExpressionType> Operator =
Parse.Char('+').Return(ExpressionType.Add)
.Or(Parse.Char('-').Return(ExpressionType.Subtract))
.Or(Parse.Char('*').Return(ExpressionType.Multiply))
.Or(Parse.Char('/').Return(ExpressionType.Divide));
private static readonly Parser<Expression> Operation =
Parse.ChainOperator(Operator, Constant, Expression.MakeBinary);
private static readonly Parser<Expression> FullExpression =
Operation.Or(Constant).End();
public static double Run(string expression)
{
var operation = FullExpression.Parse(expression);
var func = Expression.Lambda<Func<double>>(operation).Compile();
return func();
}
}
Speaker: Alexey Golub @Tyrrrz
var a = SimpleCalculator.Run("2 + 2");
var b = SimpleCalculator.Run("3.15 * 5 + 2");
var c = SimpleCalculator.Run("1 / 2 * 3");
// 4
// 17.75
// 1.5
Obtaining Expression Trees
from Lambdas
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Func<int, int, int> div =
(a, b) => a / b;
Expression<Func<int, int, int>> divExpr =
(a, b) => a / b;
Same value, different type
Console.WriteLine(divExpr.Type);
// System.Func`3[System.Int32,System.Int32,System.Int32]
Speaker: Alexey Golub @Tyrrrz
Func<int, int, int> div =
(a, b) /> a / b;
Expression<Func<int, int, int/> divExpr =
(a, b) /> a / b;
Same value, different type
foreach (var param in divExpr.Parameters)
Console.WriteLine($"Param: {param.Name} ({param.Type.Name})");
// Param: a (Int32)
// Param: b (Int32)
Speaker: Alexey Golub @Tyrrrz
Func<int, int, int> div =
(a, b) /> a / b;
Expression<Func<int, int, int/> divExpr =
(a, b) /> a / b;
Same value, different type
var div = divExpr.Compile();
var c = div(10, 2); // 5
Speaker: Alexey Golub @Tyrrrz
Func<int, int, int> div =
(a, b) => a / b;
Expression<Func<int, int, int>> divExpr =
(a, b) => a / b;
Product
Recipe
Speaker: Alexey Golub @Tyrrrz
Limitations
Func<int, int, int> div = (a, b) /> a / b;
Expression<Func<int, int, int/> divExpr = div;
Compilation error
Speaker: Alexey Golub @Tyrrrz
Limitations
Expression<Func<int, int, int/> divExpr = (a, b) />
{
var result = a / b;
return result;
};
Compilation error
Expression<Action> writeToConsole = () =>
{
Console.Write("Hello ");
Console.WriteLine("world!");
};
Compilation error
Speaker: Alexey Golub @Tyrrrz
Limitations
• Null-coalescing operator (obj?.Prop)
• Dynamic variables (dynamic)
• Asynchronous code (async/await)
• Default or named parameters (func(a, b: 5), func(a))
• Parameters passed by reference (int.TryParse("123", out var i))
• Multi-dimensional array initializers (new int[2, 2] { { 1, 2 }, { 3, 4 } })
• Assignment operations (a = 5)
• Increment and decrement (a++, a--, --a, ++a)
• Base type access (base.Prop)
• Dictionary initialization (new Dictionary<string, int> { ["foo"] = 100 })
• Unsafe code (via unsafe)
• Throw expressions (throw new Exception())
• Tuple literals ((5, x))
Can’t use any of the following:
Identifying Type Members
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
How can we get PropertyInfo of Dto.Id?
public class Dto
{
public Guid Id { get; set; }
public string Name { get; set; }
}
var idProperty = typeof(Dto).GetProperty(nameof(Dto.Id));
Console.WriteLine($"Type: {idProperty.DeclaringType.Name}");
Console.WriteLine($"Property: {idProperty.Name} ({idProperty.PropertyType.Name})");
// Type: Dto
// Property: Id (Guid)
Speaker: Alexey Golub @Tyrrrz
public class Validator<T>
{
// Add validation predicate to the list
public void AddValidation<TProp>(string propertyName, Func<TProp, bool> predicate)
{
var propertyInfo = typeof(T).GetProperty(propertyName);
if (propertyInfo is null)
throw new InvalidOperationException("Please provide a valid property name.");
// //.
}
// Evaluate all predicates
public bool Validate(T obj) { /* //. // }
/* //. //
}
var validator = new Validator<Dto>();
validator.AddValidation<Guid>(nameof(Dto.Id), id => id != Guid.Empty);
validator.AddValidation<string>(nameof(Dto.Name), name => !string.IsNullOrWhiteSpace(name));
var isValid = validator.Validate(new Dto { Id = Guid.NewGuid() }); // false
Speaker: Alexey Golub @Tyrrrz
What if we wanted to change the property type?
public class Dto
{
public int Id { get; set; }
public string Name { get; set; }
}
validator.AddValidation<Guid>(nameof(Dto.Id), id => id != Guid.Empty);
Still compiles, even though there is now an error
Speaker: Alexey Golub @Tyrrrz
public class Validator<T>
{
public void AddValidation<TProp>(
Expression<Func<T, TProp>> propertyExpression,
Func<TProp, bool> predicate)
{
var propertyInfo = (propertyExpression.Body as MemberExpression)?.Member as PropertyInfo;
if (propertyInfo is null)
throw new InvalidOperationException("Please provide a valid property expression.");
// ...
}
public bool Validate(T obj) { /* ... */ }
/* ... */
}
Expression is used to
identify a property
var validator = new Validator<Dto>();
validator.AddValidation(dto => dto.Id, id => id != Guid.Empty);
validator.AddValidation(dto => dto.Name, name => !string.IsNullOrWhiteSpace(name));
var isValid = validator.Validate(new Dto { Id = Guid.NewGuid() }); // false
Preserving Source Code in
Assertions
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
[Test]
public void IntTryParse_Test()
{
// Arrange
const string s = "123";
// Act
var result = int.TryParse(s, out var value);
// Assert
Assert.That(result, Is.True, "Parsing was unsuccessful");
Assert.That(value, Is.EqualTo(124), "Parsed value is incorrect");
}
X IntTryParse_Test [60ms]
Error Message:
Parsed value is incorrect
Expected: 124
But was: 123
Speaker: Alexey Golub @Tyrrrz
public static class AssertEx
{
public static void Express(Expression<Action> expression)
{
var act = expression.Compile();
try
{
act();
}
catch (AssertionException ex)
{
throw new AssertionException(
expression.Body.ToReadableString() +
Environment.NewLine +
ex.Message);
}
}
}
Extension method from
ReadableExpressions package
Speaker: Alexey Golub @Tyrrrz
X IntTryParse_Test [60ms]
Error Message:
Assert.That(value, Is.EqualTo(124))
Expected: 124
But was: 123
[Test]
public void IntTryParse_Test()
{
// Arrange
const string s = "123";
// Act
var result = int.TryParse(s, out var value);
// Assert
AssertEx.Express(() => Assert.That(result, Is.True));
AssertEx.Express(() => Assert.That(value, Is.EqualTo(124)));
}
Traversing and Rewriting
Expression Trees
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
public class Visitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Console.WriteLine($"Visited method call: {node}");
return base.VisitMethodCall(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine($"Visited binary expression: {node}");
return base.VisitBinary(node);
}
}
Speaker: Alexey Golub @Tyrrrz
Expression<Func<double>> expr = () => Math.Sin(Guid.NewGuid().GetHashCode()) / 10;
new Visitor().Visit(expr);
// Visited binary expression: (Sin(Convert(NewGuid().GetHashCode(), Double)) / 10)
// Visited method call: Sin(Convert(NewGuid().GetHashCode(), Double))
// Visited method call: NewGuid().GetHashCode()
// Visited method call: NewGuid()
Speaker: Alexey Golub @Tyrrrz
public class Visitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
var newMethodCall = node.Method == typeof(Math).GetMethod(nameof(Math.Sin))
? typeof(Math).GetMethod(nameof(Math.Cos))
: node.Method;
return Expression.Call(newMethodCall, node.Arguments);
}
}
Speaker: Alexey Golub @Tyrrrz
Expression<Func<double/> expr = () /> Math.Sin(Guid.NewGuid().GetHashCode()) / 10;
var result = expr.Compile()();
Console.WriteLine($"Old expression: {expr.ToReadableString()}");
Console.WriteLine($"Old result: {result}");
var newExpr = (Expression<Func<double/>) new Visitor().Visit(expr);
var newResult = newExpr.Compile()();
Console.WriteLine($"New expression: {newExpr.ToReadableString()}");
Console.WriteLine($"New result value: {newResult}");
// Old expression: () => Math.Sin((double)Guid.NewGuid().GetHashCode()) / 10d
// Old result: 0.09489518488876232
// New expression: () => Math.Cos((double)Guid.NewGuid().GetHashCode()) / 10d
// New result value: 0.07306426748550407
Transpiling Code into a Different
Language
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Expression<Action<int, int>> expr = (a, b) =>
Console.WriteLine("a + b = {0}", a + b));
var fsharpCode = FSharpTranspiler.Convert(expr);
Speaker: Alexey Golub @Tyrrrz
public static class FSharpTranspiler
{
private class Visitor : ExpressionVisitor
{
private readonly StringBuilder _buffer;
public Visitor(StringBuilder buffer)
{
_buffer = buffer;
}
// ...
}
public static string Convert<T>(Expression<T> expression)
{
var buffer = new StringBuilder();
new Visitor(buffer).Visit(expression);
return buffer.ToString();
}
}
Speaker: Alexey Golub @Tyrrrz
protected override Expression VisitLambda<T>(Expression<T> node)
{
_buffer.Append("fun (");
_buffer.AppendJoin(", ", node.Parameters.Select(p => p.Name));
_buffer.Append(") ->");
return base.VisitLambda(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(Console) &&
node.Method.Name == nameof(Console.WriteLine))
{
_buffer.Append("printfn ");
if (node.Arguments.Count > 1)
{
var format = (string) ((ConstantExpression) node.Arguments[0]).Value;
var formatValues = node.Arguments.Skip(1).ToArray();
_buffer.Append(""").Append(Regex.Replace(format, @"{d+}", "%O")).Append("" ");
_buffer.AppendJoin(" ", formatValues.Select(v => $"({v.ToReadableString()})"));
}
}
return base.VisitMethodCall(node);
}
Speaker: Alexey Golub @Tyrrrz
var fsharpCode = FSharpTranspiler.Convert<Action<int, int>>(
(a, b) => Console.WriteLine("a + b = {0}", a + b));
> let foo = fun (a, b) -> printfn "a + b = %O" (a + b)
val foo : a:int * b:int -> unit
> foo (3, 5)
a + b = 8
val it : unit = ()
// fun (a, b) > printfn "a + b = %O" (a + b)
Summary
• Expression trees are fun
• We can make reflection-heavy code much faster
• We can do late-binding with almost no performance penalties
• We can write our own runtime-compiled DSL
• We can provide refactor-safe identification for type members
• We can analyze specified lambdas and reflect on their structure
• We can rewrite existing expressions to behave differently
• We can transpile code into other languages
Speaker: Alexey Golub @Tyrrrz
Useful packages
• FastExpressionCompiler
https://github.com/dadhi/FastExpressionCompiler
Speeds up LambdaExpression.Compile()
• ReadableExpressions
https://github.com/agileobjects/ReadableExpressions
Converts expression to readable C# code
Speaker: Alexey Golub @Tyrrrz
Learn more
• Working with expression trees in C# (by me)
https://tyrrrz.me/blog/expression-trees
• Introduction to expression trees (MS docs)
https://docs.microsoft.com/en-us/dotnet/csharp/expression-trees
• Expression trees in enterprise software (Maksim Arshinov)
https://youtube.com/watch?v=J2XzsCoJM4o
Speaker: Alexey Golub @Tyrrrz
Thank you!
Speaker: Alexey Golub @Tyrrrz

More Related Content

What's hot (20)

(Greach 2015) Dsl'ing your Groovy
(Greach 2015) Dsl'ing your Groovy(Greach 2015) Dsl'ing your Groovy
(Greach 2015) Dsl'ing your Groovy
Alonso Torres
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
Paul King
 
Designing with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf IndiaDesigning with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf India
Naresha K
 
R. herves. clean code (theme)2
R. herves. clean code (theme)2R. herves. clean code (theme)2
R. herves. clean code (theme)2
saber tabatabaee
 
python beginner talk slide
python beginner talk slidepython beginner talk slide
python beginner talk slide
jonycse
 
SeaJUG March 2004 - Groovy
SeaJUG March 2004 - GroovySeaJUG March 2004 - Groovy
SeaJUG March 2004 - Groovy
Ted Leung
 
Gpars concepts explained
Gpars concepts explainedGpars concepts explained
Gpars concepts explained
Vaclav Pech
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVM
Andres Almiray
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better Java
Garth Gilmour
 
Optimizing Tcl Bytecode
Optimizing Tcl BytecodeOptimizing Tcl Bytecode
Optimizing Tcl Bytecode
Donal Fellows
 
Logic programming a ruby perspective
Logic programming a ruby perspectiveLogic programming a ruby perspective
Logic programming a ruby perspective
Norman Richards
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
Codemotion
 
Implementing pseudo-keywords through Functional Programing
Implementing pseudo-keywords through Functional ProgramingImplementing pseudo-keywords through Functional Programing
Implementing pseudo-keywords through Functional Programing
Vincent Pradeilles
 
Making an Object System with Tcl 8.5
Making an Object System with Tcl 8.5Making an Object System with Tcl 8.5
Making an Object System with Tcl 8.5
Donal Fellows
 
360|iDev
360|iDev360|iDev
360|iDev
Aijaz Ansari
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
Mario Fusco
 
Profiling and optimization
Profiling and optimizationProfiling and optimization
Profiling and optimization
g3_nittala
 
Design Pattern Observations
Design Pattern ObservationsDesign Pattern Observations
Design Pattern Observations
Dmitri Nesteruk
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Chris Richardson
 
Functional Programming In Java
Functional Programming In JavaFunctional Programming In Java
Functional Programming In Java
Andrei Solntsev
 
(Greach 2015) Dsl'ing your Groovy
(Greach 2015) Dsl'ing your Groovy(Greach 2015) Dsl'ing your Groovy
(Greach 2015) Dsl'ing your Groovy
Alonso Torres
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
Paul King
 
Designing with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf IndiaDesigning with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf India
Naresha K
 
R. herves. clean code (theme)2
R. herves. clean code (theme)2R. herves. clean code (theme)2
R. herves. clean code (theme)2
saber tabatabaee
 
python beginner talk slide
python beginner talk slidepython beginner talk slide
python beginner talk slide
jonycse
 
SeaJUG March 2004 - Groovy
SeaJUG March 2004 - GroovySeaJUG March 2004 - Groovy
SeaJUG March 2004 - Groovy
Ted Leung
 
Gpars concepts explained
Gpars concepts explainedGpars concepts explained
Gpars concepts explained
Vaclav Pech
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVM
Andres Almiray
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better Java
Garth Gilmour
 
Optimizing Tcl Bytecode
Optimizing Tcl BytecodeOptimizing Tcl Bytecode
Optimizing Tcl Bytecode
Donal Fellows
 
Logic programming a ruby perspective
Logic programming a ruby perspectiveLogic programming a ruby perspective
Logic programming a ruby perspective
Norman Richards
 
Implementing pseudo-keywords through Functional Programing
Implementing pseudo-keywords through Functional ProgramingImplementing pseudo-keywords through Functional Programing
Implementing pseudo-keywords through Functional Programing
Vincent Pradeilles
 
Making an Object System with Tcl 8.5
Making an Object System with Tcl 8.5Making an Object System with Tcl 8.5
Making an Object System with Tcl 8.5
Donal Fellows
 
Profiling and optimization
Profiling and optimizationProfiling and optimization
Profiling and optimization
g3_nittala
 
Design Pattern Observations
Design Pattern ObservationsDesign Pattern Observations
Design Pattern Observations
Dmitri Nesteruk
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Chris Richardson
 
Functional Programming In Java
Functional Programming In JavaFunctional Programming In Java
Functional Programming In Java
Andrei Solntsev
 

Similar to Oleksii Holub "Expression trees in C#" (20)

C# 7.x What's new and what's coming with C# 8
C# 7.x What's new and what's coming with C# 8C# 7.x What's new and what's coming with C# 8
C# 7.x What's new and what's coming with C# 8
Christian Nagel
 
Clean Code 2
Clean Code 2Clean Code 2
Clean Code 2
Fredrik Wendt
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
Hans Höchtl
 
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Oleksii Holub
 
Clean code _v2003
 Clean code _v2003 Clean code _v2003
Clean code _v2003
R696
 
Clean Code
Clean CodeClean Code
Clean Code
Nascenia IT
 
TypeScript Best Practices
TypeScript Best PracticesTypeScript Best Practices
TypeScript Best Practices
felixbillon
 
Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008
Guillaume Laforge
 
C# - What's next
C# - What's nextC# - What's next
C# - What's next
Christian Nagel
 
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguajeKotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Víctor Leonel Orozco López
 
XAML/C# to HTML/JS
XAML/C# to HTML/JSXAML/C# to HTML/JS
XAML/C# to HTML/JS
Michael Haberman
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
HamletDRC
 
Java gets a closure
Java gets a closureJava gets a closure
Java gets a closure
Tomasz Kowalczewski
 
TechTalk - Dotnet
TechTalk - DotnetTechTalk - Dotnet
TechTalk - Dotnet
heinrich.wendel
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 
Bc0037
Bc0037Bc0037
Bc0037
hayerpa
 
Scala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowScala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To Know
Lightbend
 
Groovy
GroovyGroovy
Groovy
Zen Urban
 
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Tudor Dragan
 
Clojure And Swing
Clojure And SwingClojure And Swing
Clojure And Swing
Skills Matter
 
C# 7.x What's new and what's coming with C# 8
C# 7.x What's new and what's coming with C# 8C# 7.x What's new and what's coming with C# 8
C# 7.x What's new and what's coming with C# 8
Christian Nagel
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
Hans Höchtl
 
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Oleksii Holub
 
Clean code _v2003
 Clean code _v2003 Clean code _v2003
Clean code _v2003
R696
 
TypeScript Best Practices
TypeScript Best PracticesTypeScript Best Practices
TypeScript Best Practices
felixbillon
 
Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008
Guillaume Laforge
 
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguajeKotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Víctor Leonel Orozco López
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
HamletDRC
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 
Scala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowScala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To Know
Lightbend
 
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Tudor Dragan
 

More from Fwdays (20)

"Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu...
"Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu..."Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu...
"Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu...
Fwdays
 
"AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea...
"AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea..."AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea...
"AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea...
Fwdays
 
"Constructive Interaction During Emotional Burnout: With Local and Internatio...
"Constructive Interaction During Emotional Burnout: With Local and Internatio..."Constructive Interaction During Emotional Burnout: With Local and Internatio...
"Constructive Interaction During Emotional Burnout: With Local and Internatio...
Fwdays
 
"Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil...
"Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil..."Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil...
"Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil...
Fwdays
 
"39 offers for my mentees in a year. How to create a professional environment...
"39 offers for my mentees in a year. How to create a professional environment..."39 offers for my mentees in a year. How to create a professional environment...
"39 offers for my mentees in a year. How to create a professional environment...
Fwdays
 
"From “doing tasks” to leadership: how to adapt management style to the conte...
"From “doing tasks” to leadership: how to adapt management style to the conte..."From “doing tasks” to leadership: how to adapt management style to the conte...
"From “doing tasks” to leadership: how to adapt management style to the conte...
Fwdays
 
[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...
[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...
[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...
Fwdays
 
[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...
[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...
[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...
Fwdays
 
[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...
[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...
[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...
Fwdays
 
"Dialogue about fakapas: how to pass an interview without unnecessary mistake...
"Dialogue about fakapas: how to pass an interview without unnecessary mistake..."Dialogue about fakapas: how to pass an interview without unnecessary mistake...
"Dialogue about fakapas: how to pass an interview without unnecessary mistake...
Fwdays
 
"Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest...
"Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest..."Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest...
"Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest...
Fwdays
 
"Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem...
"Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem..."Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem...
"Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem...
Fwdays
 
"Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska...
"Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska..."Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska...
"Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska...
Fwdays
 
"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy
"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy
"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy
Fwdays
 
"Building Trust: Strengthening Your Software Supply Chain Security", Serhii V...
"Building Trust: Strengthening Your Software Supply Chain Security", Serhii V..."Building Trust: Strengthening Your Software Supply Chain Security", Serhii V...
"Building Trust: Strengthening Your Software Supply Chain Security", Serhii V...
Fwdays
 
"10 Pitfalls of a Platform Team", Yura Rochniak
"10 Pitfalls of a Platform Team", Yura Rochniak"10 Pitfalls of a Platform Team", Yura Rochniak
"10 Pitfalls of a Platform Team", Yura Rochniak
Fwdays
 
"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak
"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak
"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak
Fwdays
 
"Zero-sales lost — Incident Management", Igor Drozd
"Zero-sales lost — Incident Management", Igor Drozd"Zero-sales lost — Incident Management", Igor Drozd
"Zero-sales lost — Incident Management", Igor Drozd
Fwdays
 
"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap
"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap
"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap
Fwdays
 
"Kubernetes operators. How we migrated Release Management to controllers", De...
"Kubernetes operators. How we migrated Release Management to controllers", De..."Kubernetes operators. How we migrated Release Management to controllers", De...
"Kubernetes operators. How we migrated Release Management to controllers", De...
Fwdays
 
"Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu...
"Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu..."Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu...
"Scaling Product Mindset: From Individual Ideas to Team Culture", Oksana Holu...
Fwdays
 
"AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea...
"AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea..."AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea...
"AI-Driven Automation for High-Performing Teams: Optimize Routine Tasks & Lea...
Fwdays
 
"Constructive Interaction During Emotional Burnout: With Local and Internatio...
"Constructive Interaction During Emotional Burnout: With Local and Internatio..."Constructive Interaction During Emotional Burnout: With Local and Internatio...
"Constructive Interaction During Emotional Burnout: With Local and Internatio...
Fwdays
 
"Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil...
"Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil..."Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil...
"Perfectionisin: What Does the Medicine for Perfectionism Look Like?", Manoil...
Fwdays
 
"39 offers for my mentees in a year. How to create a professional environment...
"39 offers for my mentees in a year. How to create a professional environment..."39 offers for my mentees in a year. How to create a professional environment...
"39 offers for my mentees in a year. How to create a professional environment...
Fwdays
 
"From “doing tasks” to leadership: how to adapt management style to the conte...
"From “doing tasks” to leadership: how to adapt management style to the conte..."From “doing tasks” to leadership: how to adapt management style to the conte...
"From “doing tasks” to leadership: how to adapt management style to the conte...
Fwdays
 
[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...
[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...
[QUICK TALK] "Why Some Teams Grow Better Under Pressure", Oleksandr Marchenko...
Fwdays
 
[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...
[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...
[QUICK TALK] "How to study to acquire a skill, not a certificate?", Uliana Du...
Fwdays
 
[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...
[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...
[QUICK TALK] "Coaching 101: How to Identify and Develop Your Leadership Quali...
Fwdays
 
"Dialogue about fakapas: how to pass an interview without unnecessary mistake...
"Dialogue about fakapas: how to pass an interview without unnecessary mistake..."Dialogue about fakapas: how to pass an interview without unnecessary mistake...
"Dialogue about fakapas: how to pass an interview without unnecessary mistake...
Fwdays
 
"Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest...
"Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest..."Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest...
"Conflicts within a Team: Not an Enemy, But an Opportunity for Growth", Orest...
Fwdays
 
"Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem...
"Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem..."Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem...
"Surfing the IT Waves: How Not to Drown in the Information Ocean", Serhii Nem...
Fwdays
 
"Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska...
"Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska..."Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska...
"Seeing vs. Understanding: The Hidden Psychology of Design", Irene Shkarovska...
Fwdays
 
"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy
"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy
"Spin-up pgbouncer for fun and profit", Vitaliy Kharytonskiy
Fwdays
 
"Building Trust: Strengthening Your Software Supply Chain Security", Serhii V...
"Building Trust: Strengthening Your Software Supply Chain Security", Serhii V..."Building Trust: Strengthening Your Software Supply Chain Security", Serhii V...
"Building Trust: Strengthening Your Software Supply Chain Security", Serhii V...
Fwdays
 
"10 Pitfalls of a Platform Team", Yura Rochniak
"10 Pitfalls of a Platform Team", Yura Rochniak"10 Pitfalls of a Platform Team", Yura Rochniak
"10 Pitfalls of a Platform Team", Yura Rochniak
Fwdays
 
"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak
"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak
"Reality of Managing 100+ “Managed” RDS Postgres Databases", Mykyta Hlushak
Fwdays
 
"Zero-sales lost — Incident Management", Igor Drozd
"Zero-sales lost — Incident Management", Igor Drozd"Zero-sales lost — Incident Management", Igor Drozd
"Zero-sales lost — Incident Management", Igor Drozd
Fwdays
 
"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap
"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap
"Turning Kubernetes into a full-fledged private cloud", Volodymyr Tsap
Fwdays
 
"Kubernetes operators. How we migrated Release Management to controllers", De...
"Kubernetes operators. How we migrated Release Management to controllers", De..."Kubernetes operators. How we migrated Release Management to controllers", De...
"Kubernetes operators. How we migrated Release Management to controllers", De...
Fwdays
 

Recently uploaded (20)

Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025
Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025
Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025
Sugarlab AI
 
Research Data Management (RDM): the management of dat in the research process
Research Data Management (RDM): the management of dat in the research processResearch Data Management (RDM): the management of dat in the research process
Research Data Management (RDM): the management of dat in the research process
HeilaPienaar
 
STRING FUNCTIONS IN JAVA BY N SARATH KUMAR
STRING FUNCTIONS IN JAVA BY N SARATH KUMARSTRING FUNCTIONS IN JAVA BY N SARATH KUMAR
STRING FUNCTIONS IN JAVA BY N SARATH KUMAR
Sarathkumar Narsupalli
 
STARLINK-JIO-AIRTEL Security issues to Ponder
STARLINK-JIO-AIRTEL Security issues to PonderSTARLINK-JIO-AIRTEL Security issues to Ponder
STARLINK-JIO-AIRTEL Security issues to Ponder
anupriti
 
Innovative Web Design | Malachite Technologies
Innovative Web Design | Malachite TechnologiesInnovative Web Design | Malachite Technologies
Innovative Web Design | Malachite Technologies
malachitetechnologie1
 
Artificial Neural Networks, basics, its variations and examples
Artificial Neural Networks, basics, its variations and examplesArtificial Neural Networks, basics, its variations and examples
Artificial Neural Networks, basics, its variations and examples
anandsimple
 
202408_JAWSPANKRATION_Introduction_of_Minaden.pdf
202408_JAWSPANKRATION_Introduction_of_Minaden.pdf202408_JAWSPANKRATION_Introduction_of_Minaden.pdf
202408_JAWSPANKRATION_Introduction_of_Minaden.pdf
NTTDOCOMO-ServiceInnovation
 
Why Outsource Accounting to India A Smart Business Move!.pdf
Why Outsource Accounting to India A Smart Business Move!.pdfWhy Outsource Accounting to India A Smart Business Move!.pdf
Why Outsource Accounting to India A Smart Business Move!.pdf
anjelinajones6811
 
Mastering Azure Durable Functions - Building Resilient and Scalable Workflows
Mastering Azure Durable Functions - Building Resilient and Scalable WorkflowsMastering Azure Durable Functions - Building Resilient and Scalable Workflows
Mastering Azure Durable Functions - Building Resilient and Scalable Workflows
Callon Campbell
 
Beyond the life of a CISO - Head of Trust at GDG Kathmandu Monthly Meetup
Beyond the life of a CISO -  Head of Trust at GDG Kathmandu Monthly MeetupBeyond the life of a CISO -  Head of Trust at GDG Kathmandu Monthly Meetup
Beyond the life of a CISO - Head of Trust at GDG Kathmandu Monthly Meetup
GDG Kathmandu
 
The Road to SAP S4HANA Cloud with SAP Activate.pptx
The Road to SAP S4HANA Cloud with SAP Activate.pptxThe Road to SAP S4HANA Cloud with SAP Activate.pptx
The Road to SAP S4HANA Cloud with SAP Activate.pptx
zsbaranyai
 
Transactional Outbox & Inbox Patterns.pptx
Transactional Outbox & Inbox Patterns.pptxTransactional Outbox & Inbox Patterns.pptx
Transactional Outbox & Inbox Patterns.pptx
Maysam Mousa
 
Automating Behavior-Driven Development: Boosting Productivity with Template-D...
Automating Behavior-Driven Development: Boosting Productivity with Template-D...Automating Behavior-Driven Development: Boosting Productivity with Template-D...
Automating Behavior-Driven Development: Boosting Productivity with Template-D...
DOCOMO Innovations, Inc.
 
Next.js Development: The Ultimate Solution for High-Performance Web Apps
Next.js Development: The Ultimate Solution for High-Performance Web AppsNext.js Development: The Ultimate Solution for High-Performance Web Apps
Next.js Development: The Ultimate Solution for High-Performance Web Apps
rwinfotech31
 
Automated Engineering of Domain-Specific Metamorphic Testing Environments
Automated Engineering of Domain-Specific Metamorphic Testing EnvironmentsAutomated Engineering of Domain-Specific Metamorphic Testing Environments
Automated Engineering of Domain-Specific Metamorphic Testing Environments
Pablo Gómez Abajo
 
All-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-World
All-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-WorldAll-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-World
All-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-World
Safe Software
 
Leadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly Meetup
Leadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly MeetupLeadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly Meetup
Leadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly Meetup
GDG Kathmandu
 
SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...
SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...
SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...
DianaGray10
 
GDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AI
GDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AIGDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AI
GDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AI
James Anderson
 
Solana Developer Hiring for Enterprises Key Considerations.pdf
Solana Developer Hiring for Enterprises Key Considerations.pdfSolana Developer Hiring for Enterprises Key Considerations.pdf
Solana Developer Hiring for Enterprises Key Considerations.pdf
Lisa ward
 
Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025
Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025
Sugarlab AI: How Much Does an XXX AI Porn Generator Cost in 2025
Sugarlab AI
 
Research Data Management (RDM): the management of dat in the research process
Research Data Management (RDM): the management of dat in the research processResearch Data Management (RDM): the management of dat in the research process
Research Data Management (RDM): the management of dat in the research process
HeilaPienaar
 
STRING FUNCTIONS IN JAVA BY N SARATH KUMAR
STRING FUNCTIONS IN JAVA BY N SARATH KUMARSTRING FUNCTIONS IN JAVA BY N SARATH KUMAR
STRING FUNCTIONS IN JAVA BY N SARATH KUMAR
Sarathkumar Narsupalli
 
STARLINK-JIO-AIRTEL Security issues to Ponder
STARLINK-JIO-AIRTEL Security issues to PonderSTARLINK-JIO-AIRTEL Security issues to Ponder
STARLINK-JIO-AIRTEL Security issues to Ponder
anupriti
 
Innovative Web Design | Malachite Technologies
Innovative Web Design | Malachite TechnologiesInnovative Web Design | Malachite Technologies
Innovative Web Design | Malachite Technologies
malachitetechnologie1
 
Artificial Neural Networks, basics, its variations and examples
Artificial Neural Networks, basics, its variations and examplesArtificial Neural Networks, basics, its variations and examples
Artificial Neural Networks, basics, its variations and examples
anandsimple
 
202408_JAWSPANKRATION_Introduction_of_Minaden.pdf
202408_JAWSPANKRATION_Introduction_of_Minaden.pdf202408_JAWSPANKRATION_Introduction_of_Minaden.pdf
202408_JAWSPANKRATION_Introduction_of_Minaden.pdf
NTTDOCOMO-ServiceInnovation
 
Why Outsource Accounting to India A Smart Business Move!.pdf
Why Outsource Accounting to India A Smart Business Move!.pdfWhy Outsource Accounting to India A Smart Business Move!.pdf
Why Outsource Accounting to India A Smart Business Move!.pdf
anjelinajones6811
 
Mastering Azure Durable Functions - Building Resilient and Scalable Workflows
Mastering Azure Durable Functions - Building Resilient and Scalable WorkflowsMastering Azure Durable Functions - Building Resilient and Scalable Workflows
Mastering Azure Durable Functions - Building Resilient and Scalable Workflows
Callon Campbell
 
Beyond the life of a CISO - Head of Trust at GDG Kathmandu Monthly Meetup
Beyond the life of a CISO -  Head of Trust at GDG Kathmandu Monthly MeetupBeyond the life of a CISO -  Head of Trust at GDG Kathmandu Monthly Meetup
Beyond the life of a CISO - Head of Trust at GDG Kathmandu Monthly Meetup
GDG Kathmandu
 
The Road to SAP S4HANA Cloud with SAP Activate.pptx
The Road to SAP S4HANA Cloud with SAP Activate.pptxThe Road to SAP S4HANA Cloud with SAP Activate.pptx
The Road to SAP S4HANA Cloud with SAP Activate.pptx
zsbaranyai
 
Transactional Outbox & Inbox Patterns.pptx
Transactional Outbox & Inbox Patterns.pptxTransactional Outbox & Inbox Patterns.pptx
Transactional Outbox & Inbox Patterns.pptx
Maysam Mousa
 
Automating Behavior-Driven Development: Boosting Productivity with Template-D...
Automating Behavior-Driven Development: Boosting Productivity with Template-D...Automating Behavior-Driven Development: Boosting Productivity with Template-D...
Automating Behavior-Driven Development: Boosting Productivity with Template-D...
DOCOMO Innovations, Inc.
 
Next.js Development: The Ultimate Solution for High-Performance Web Apps
Next.js Development: The Ultimate Solution for High-Performance Web AppsNext.js Development: The Ultimate Solution for High-Performance Web Apps
Next.js Development: The Ultimate Solution for High-Performance Web Apps
rwinfotech31
 
Automated Engineering of Domain-Specific Metamorphic Testing Environments
Automated Engineering of Domain-Specific Metamorphic Testing EnvironmentsAutomated Engineering of Domain-Specific Metamorphic Testing Environments
Automated Engineering of Domain-Specific Metamorphic Testing Environments
Pablo Gómez Abajo
 
All-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-World
All-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-WorldAll-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-World
All-Data, Any-AI Integration: FME & Amazon Bedrock in the Real-World
Safe Software
 
Leadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly Meetup
Leadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly MeetupLeadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly Meetup
Leadership Spectrum by Sonam Sherpa at GDG Kathmandu March Monthly Meetup
GDG Kathmandu
 
SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...
SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...
SAP Automation with UiPath: Solution Accelerators and Best Practices - Part 6...
DianaGray10
 
GDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AI
GDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AIGDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AI
GDG Cloud Southlake #41: Shay Levi: Beyond the Hype:How Enterprises Are Using AI
James Anderson
 
Solana Developer Hiring for Enterprises Key Considerations.pdf
Solana Developer Hiring for Enterprises Key Considerations.pdfSolana Developer Hiring for Enterprises Key Considerations.pdf
Solana Developer Hiring for Enterprises Key Considerations.pdf
Lisa ward
 

Oleksii Holub "Expression trees in C#"

  • 2. Speaker: Alexey Golub @Tyrrrz Expression Trees in C# I heard you like code, so we put code in your code so you can code while you code
  • 3. /whois ${speaker} Speaker: Alexey Golub @Tyrrrz • Open-source developer ✨ • Conference speaker & blogger 🌐️ • C#, F#, JavaScript 💻 • Cloud & web ☁️ • Automation & DevOps ⚙️
  • 4. What is an expression tree? Speaker: Alexey Golub @Tyrrrz
  • 5. Speaker: Alexey Golub @Tyrrrz +Constant (2) Constant (3) Plus operator 2 3 Binary expression
  • 6. Speaker: Alexey Golub @Tyrrrz !string.IsNullOrWhiteSpace(personName) ? "Greetings, " + personName : null; string? GetGreeting(string personName) { return }
  • 7. Speaker: Alexey Golub @Tyrrrz "Greetings, " ! personName string.IsNullOrWhiteSpace( ) null;: ? + personName OPERATOR "NOT" METHOD CALL PARAMETER PARAMETERCONSTANT OPERATOR "ADD" CONSTANT
  • 8. Speaker: Alexey Golub @Tyrrrz { Ternary conditional } { + } TRUE { null } FALSE { Method call } CONDITION { string.IsNullOrWhiteSpace } { personName } { personName } { "Greetings, " } { ! }
  • 9. Speaker: Alexey Golub @Tyrrrz Expression Tree describes the structure of an expression
  • 10. Expression Trees in .NET • Object model represented by types deriving from System.Linq.Expressions.Expression • Can be constructed manually using factory methods • Can be inferred from lambdas by the compiler • Lambda expression trees can be converted into delegates Speaker: Alexey Golub @Tyrrrz
  • 13. Speaker: Alexey Golub @Tyrrrz Expression.Constant(...) ConstantExpression Expression.New(...) NewExpression Expression.Assign(...) BinaryExpression Expression.Equal(...) BinaryExpression Expression.Call(...) MethodCallExpression Expression.Condition(...) ConditionalExpression Expression.Loop(...) LoopExpression ...
  • 14. Speaker: Alexey Golub @Tyrrrz !string.IsNullOrWhiteSpace(personName) ? "Greetings, " + personName : null; Let’s recreate our expression dynamically
  • 15. Speaker: Alexey Golub @Tyrrrz public Func<string, string?> ConstructGreetingFunction() { var personNameParameter = Expression.Parameter(typeof(string), "personName"); var isNullOrWhiteSpaceMethod = typeof(string) .GetMethod(nameof(string.IsNullOrWhiteSpace)); var condition = Expression.Not( Expression.Call(isNullOrWhiteSpaceMethod, personNameParameter)); var trueClause = Expression.Add( Expression.Constant("Greetings, "), personNameParameter); var falseClause = Expression.Constant(null, typeof(string)); var conditional = Expression.Condition(condition, trueClause, falseClause); var lambda = Expression.Lambda<Func<string, string?>>(conditional, personNameParameter); return lambda.Compile(); }
  • 16. Speaker: Alexey Golub @Tyrrrz var getGreeting = ConstructGreetingFunction(); var greetingForJohn = getGreeting("John"); The binary operator Add is not defined for the types 'System.String' and 'System.String'.
  • 17. Speaker: Alexey Golub @Tyrrrz We need to call string.Concat() directly var concatMethod = typeof(string) .GetMethod(nameof(string.Concat), new[] {typeof(string), typeof(string)}); var trueClause = Expression.Call( concatMethod, Expression.Constant("Greetings, "), personNameParameter);
  • 18. Speaker: Alexey Golub @Tyrrrz var getGreetings = ConstructGreetingFunction(); var greetingsForJohn = getGreetings("John"); var greetingsForNobody = getGreetings(" "); // "Greetings, John" // <null>
  • 20. Speaker: Alexey Golub @Tyrrrz How can we invoke Execute() from the outside? public class Command { private int Execute() /> 42; } public static int CallExecute(Command command) => (int) typeof(Command) .GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance) .Invoke(command, null);
  • 21. Speaker: Alexey Golub @Tyrrrz public static class ReflectionCached { private static MethodInfo ExecuteMethod { get; } = typeof(Command) .GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance); public static int CallExecute(Command command) => (int) ExecuteMethod.Invoke(command, null); } public static class Re/lectionDelegate { private static MethodInfo ExecuteMethod { get; } = typeof(Command) .GetMethod("Execute", Binding/lags.NonPublic | Binding/lags.Instance); private static Func<Command, int> Impl { get; } = (Func<Command, int>) Delegate .CreateDelegate(typeof(Func<Command, int>), ExecuteMethod); public static int CallExecute(Command command) /> Impl(command); } Using cached MethodInfo Using Delegate.CreateDelegate
  • 22. public static class ExpressionTrees { private static MethodInfo ExecuteMethod { get; } = typeof(Command) .GetMethod("Execute", Binding/lags.NonPublic | Binding/lags.Instance); private static Func<Command, int> Impl { get; } static ExpressionTrees() { var instance = Expression.Parameter(typeof(Command)); var call = Expression.Call(instance, ExecuteMethod); Impl = Expression.Lambda<Func<Command, int/>(call, instance).Compile(); } public static int CallExecute(Command command) /> Impl(command); } Speaker: Alexey Golub @Tyrrrz Lazy thread-safe initialization via static constructor
  • 23. Speaker: Alexey Golub @Tyrrrz public class Benchmarks { [Benchmark(Description = "Reflection", Baseline = true)] public int Reflection() => (int) typeof(Command) .GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance) .Invoke(new Command(), null); [Benchmark(Description = "Reflection (cached)")] public int Cached() => ReflectionCached.CallExecute(new Command()); [Benchmark(Description = "Reflection (delegate)")] public int Delegate() => ReflectionDelegate.CallExecute(new Command()); [Benchmark(Description = "Expressions")] public int Expressions() => ExpressionTrees.CallExecute(new Command()); public static void Main() => BenchmarkRunner.Run<Benchmarks>(); } | Method | Mean | Error | StdDev | Ratio | |---------------------- |-----------:|----------:|----------:|------:| | Reflection | 192.975 ns | 1.6802 ns | 1.4895 ns | 1.00 | | Reflection (cached) | 123.762 ns | 1.1063 ns | 1.0349 ns | 0.64 | | Reflection (delegate) | 6.419 ns | 0.0646 ns | 0.0605 ns | 0.03 | | Expressions | 5.383 ns | 0.0433 ns | 0.0383 ns | 0.03 |
  • 25. Speaker: Alexey Golub @Tyrrrz What can we do to support multiple types? public int ThreeFourths(int x) => 3 * x / 4; public int ThreeFourths(int x) => 3 * x / 4; public long ThreeFourths(long x) => 3 * x / 4; public float ThreeFourths(float x) => 3 * x / 4; public double ThreeFourths(double x) => 3 * x / 4; public decimal ThreeFourths(decimal x) => 3 * x / 4; public T ThreeFourths<T>(T x) /> 3 * x / 4; But we actually want something like this instead
  • 26. Speaker: Alexey Golub @Tyrrrz public T ThreeFourths<T>(T x) { var param = Expression.Parameter(typeof(T)); var three = Expression.Convert(Expression.Constant(3), typeof(T)); var four = Expression.Convert(Expression.Constant(4), typeof(T)); var operation = Expression.Divide(Expression.Multiply(param, three), four); var lambda = Expression.Lambda<Func<T, T>>(operation, param); var func = lambda.Compile(); return func(x); } var a = ThreeFourths(18); // 13 var b = ThreeFourths(6.66); // 4.995 var c = ThreeFourths(100M); // 75M
  • 27. Speaker: Alexey Golub @Tyrrrz public dynamic ThreeFourths(dynamic x) /> 3 * x / 4; Wait, how is it different from this?
  • 28. public static class ThreeFourths { private static class Impl<T> { public static Func<T, T> Of { get; } static Impl() { var param = Expression.Parameter(typeof(T)); var three = Expression.Convert(Expression.Constant(3), typeof(T)); var four = Expression.Convert(Expression.Constant(4), typeof(T)); var operation = Expression.Divide(Expression.Multiply(param, three), four); var lambda = Expression.Lambda<Func<T, T>>(operation, param); Of = lambda.Compile(); } } public static T Of<T>(T x) => Impl<T>.Of(x); } Speaker: Alexey Golub @Tyrrrz Generic, thread-safe lazy initialization
  • 29. Speaker: Alexey Golub @Tyrrrz public class Benchmarks { [Benchmark(Description = "Static", Baseline = true)] [Arguments(13.37)] public double Static(double x) /> 3 * x / 4; [Benchmark(Description = "Expressions")] [Arguments(13.37)] public double Expressions(double x) /> ThreeFourths.Of(x); [Benchmark(Description = "Dynamic")] [Arguments(13.37)] public dynamic Dynamic(dynamic x) /> 3 * x / 4; public static void Main() /> BenchmarkRunner.Run<Benchmarks>(); } | Method | x | Mean | Error | StdDev | Ratio | RatioSD | |------------ |------ |-----------:|----------:|----------:|------:|--------:| | Static | 13.37 | 0.6077 ns | 0.0176 ns | 0.0147 ns | 1.00 | 0.00 | | Expressions | 13.37 | 1.9510 ns | 0.0163 ns | 0.0145 ns | 3.21 | 0.08 | | Dynamic | 13.37 | 19.3267 ns | 0.1512 ns | 0.1340 ns | 31.82 | 0.78 |
  • 30. Parsing DSLs into Expression Trees Speaker: Alexey Golub @Tyrrrz
  • 31. Speaker: Alexey Golub @Tyrrrz public static class SimpleCalculator { private static readonly Parser<Expression> Constant = Parse.DecimalInvariant .Select(n => double.Parse(n, CultureInfo.InvariantCulture)) .Select(n => Expression.Constant(n, typeof(double))) .Token(); private static readonly Parser<ExpressionType> Operator = Parse.Char('+').Return(ExpressionType.Add) .Or(Parse.Char('-').Return(ExpressionType.Subtract)) .Or(Parse.Char('*').Return(ExpressionType.Multiply)) .Or(Parse.Char('/').Return(ExpressionType.Divide)); private static readonly Parser<Expression> Operation = Parse.ChainOperator(Operator, Constant, Expression.MakeBinary); private static readonly Parser<Expression> FullExpression = Operation.Or(Constant).End(); public static double Run(string expression) { var operation = FullExpression.Parse(expression); var func = Expression.Lambda<Func<double>>(operation).Compile(); return func(); } }
  • 32. Speaker: Alexey Golub @Tyrrrz var a = SimpleCalculator.Run("2 + 2"); var b = SimpleCalculator.Run("3.15 * 5 + 2"); var c = SimpleCalculator.Run("1 / 2 * 3"); // 4 // 17.75 // 1.5
  • 33. Obtaining Expression Trees from Lambdas Speaker: Alexey Golub @Tyrrrz
  • 34. Speaker: Alexey Golub @Tyrrrz Func<int, int, int> div = (a, b) => a / b; Expression<Func<int, int, int>> divExpr = (a, b) => a / b; Same value, different type Console.WriteLine(divExpr.Type); // System.Func`3[System.Int32,System.Int32,System.Int32]
  • 35. Speaker: Alexey Golub @Tyrrrz Func<int, int, int> div = (a, b) /> a / b; Expression<Func<int, int, int/> divExpr = (a, b) /> a / b; Same value, different type foreach (var param in divExpr.Parameters) Console.WriteLine($"Param: {param.Name} ({param.Type.Name})"); // Param: a (Int32) // Param: b (Int32)
  • 36. Speaker: Alexey Golub @Tyrrrz Func<int, int, int> div = (a, b) /> a / b; Expression<Func<int, int, int/> divExpr = (a, b) /> a / b; Same value, different type var div = divExpr.Compile(); var c = div(10, 2); // 5
  • 37. Speaker: Alexey Golub @Tyrrrz Func<int, int, int> div = (a, b) => a / b; Expression<Func<int, int, int>> divExpr = (a, b) => a / b; Product Recipe
  • 38. Speaker: Alexey Golub @Tyrrrz Limitations Func<int, int, int> div = (a, b) /> a / b; Expression<Func<int, int, int/> divExpr = div; Compilation error
  • 39. Speaker: Alexey Golub @Tyrrrz Limitations Expression<Func<int, int, int/> divExpr = (a, b) /> { var result = a / b; return result; }; Compilation error Expression<Action> writeToConsole = () => { Console.Write("Hello "); Console.WriteLine("world!"); }; Compilation error
  • 40. Speaker: Alexey Golub @Tyrrrz Limitations • Null-coalescing operator (obj?.Prop) • Dynamic variables (dynamic) • Asynchronous code (async/await) • Default or named parameters (func(a, b: 5), func(a)) • Parameters passed by reference (int.TryParse("123", out var i)) • Multi-dimensional array initializers (new int[2, 2] { { 1, 2 }, { 3, 4 } }) • Assignment operations (a = 5) • Increment and decrement (a++, a--, --a, ++a) • Base type access (base.Prop) • Dictionary initialization (new Dictionary<string, int> { ["foo"] = 100 }) • Unsafe code (via unsafe) • Throw expressions (throw new Exception()) • Tuple literals ((5, x)) Can’t use any of the following:
  • 41. Identifying Type Members Speaker: Alexey Golub @Tyrrrz
  • 42. Speaker: Alexey Golub @Tyrrrz How can we get PropertyInfo of Dto.Id? public class Dto { public Guid Id { get; set; } public string Name { get; set; } } var idProperty = typeof(Dto).GetProperty(nameof(Dto.Id)); Console.WriteLine($"Type: {idProperty.DeclaringType.Name}"); Console.WriteLine($"Property: {idProperty.Name} ({idProperty.PropertyType.Name})"); // Type: Dto // Property: Id (Guid)
  • 43. Speaker: Alexey Golub @Tyrrrz public class Validator<T> { // Add validation predicate to the list public void AddValidation<TProp>(string propertyName, Func<TProp, bool> predicate) { var propertyInfo = typeof(T).GetProperty(propertyName); if (propertyInfo is null) throw new InvalidOperationException("Please provide a valid property name."); // //. } // Evaluate all predicates public bool Validate(T obj) { /* //. // } /* //. // } var validator = new Validator<Dto>(); validator.AddValidation<Guid>(nameof(Dto.Id), id => id != Guid.Empty); validator.AddValidation<string>(nameof(Dto.Name), name => !string.IsNullOrWhiteSpace(name)); var isValid = validator.Validate(new Dto { Id = Guid.NewGuid() }); // false
  • 44. Speaker: Alexey Golub @Tyrrrz What if we wanted to change the property type? public class Dto { public int Id { get; set; } public string Name { get; set; } } validator.AddValidation<Guid>(nameof(Dto.Id), id => id != Guid.Empty); Still compiles, even though there is now an error
  • 45. Speaker: Alexey Golub @Tyrrrz public class Validator<T> { public void AddValidation<TProp>( Expression<Func<T, TProp>> propertyExpression, Func<TProp, bool> predicate) { var propertyInfo = (propertyExpression.Body as MemberExpression)?.Member as PropertyInfo; if (propertyInfo is null) throw new InvalidOperationException("Please provide a valid property expression."); // ... } public bool Validate(T obj) { /* ... */ } /* ... */ } Expression is used to identify a property var validator = new Validator<Dto>(); validator.AddValidation(dto => dto.Id, id => id != Guid.Empty); validator.AddValidation(dto => dto.Name, name => !string.IsNullOrWhiteSpace(name)); var isValid = validator.Validate(new Dto { Id = Guid.NewGuid() }); // false
  • 46. Preserving Source Code in Assertions Speaker: Alexey Golub @Tyrrrz
  • 47. Speaker: Alexey Golub @Tyrrrz [Test] public void IntTryParse_Test() { // Arrange const string s = "123"; // Act var result = int.TryParse(s, out var value); // Assert Assert.That(result, Is.True, "Parsing was unsuccessful"); Assert.That(value, Is.EqualTo(124), "Parsed value is incorrect"); } X IntTryParse_Test [60ms] Error Message: Parsed value is incorrect Expected: 124 But was: 123
  • 48. Speaker: Alexey Golub @Tyrrrz public static class AssertEx { public static void Express(Expression<Action> expression) { var act = expression.Compile(); try { act(); } catch (AssertionException ex) { throw new AssertionException( expression.Body.ToReadableString() + Environment.NewLine + ex.Message); } } } Extension method from ReadableExpressions package
  • 49. Speaker: Alexey Golub @Tyrrrz X IntTryParse_Test [60ms] Error Message: Assert.That(value, Is.EqualTo(124)) Expected: 124 But was: 123 [Test] public void IntTryParse_Test() { // Arrange const string s = "123"; // Act var result = int.TryParse(s, out var value); // Assert AssertEx.Express(() => Assert.That(result, Is.True)); AssertEx.Express(() => Assert.That(value, Is.EqualTo(124))); }
  • 50. Traversing and Rewriting Expression Trees Speaker: Alexey Golub @Tyrrrz
  • 52. Speaker: Alexey Golub @Tyrrrz public class Visitor : ExpressionVisitor { protected override Expression VisitMethodCall(MethodCallExpression node) { Console.WriteLine($"Visited method call: {node}"); return base.VisitMethodCall(node); } protected override Expression VisitBinary(BinaryExpression node) { Console.WriteLine($"Visited binary expression: {node}"); return base.VisitBinary(node); } }
  • 53. Speaker: Alexey Golub @Tyrrrz Expression<Func<double>> expr = () => Math.Sin(Guid.NewGuid().GetHashCode()) / 10; new Visitor().Visit(expr); // Visited binary expression: (Sin(Convert(NewGuid().GetHashCode(), Double)) / 10) // Visited method call: Sin(Convert(NewGuid().GetHashCode(), Double)) // Visited method call: NewGuid().GetHashCode() // Visited method call: NewGuid()
  • 54. Speaker: Alexey Golub @Tyrrrz public class Visitor : ExpressionVisitor { protected override Expression VisitMethodCall(MethodCallExpression node) { var newMethodCall = node.Method == typeof(Math).GetMethod(nameof(Math.Sin)) ? typeof(Math).GetMethod(nameof(Math.Cos)) : node.Method; return Expression.Call(newMethodCall, node.Arguments); } }
  • 55. Speaker: Alexey Golub @Tyrrrz Expression<Func<double/> expr = () /> Math.Sin(Guid.NewGuid().GetHashCode()) / 10; var result = expr.Compile()(); Console.WriteLine($"Old expression: {expr.ToReadableString()}"); Console.WriteLine($"Old result: {result}"); var newExpr = (Expression<Func<double/>) new Visitor().Visit(expr); var newResult = newExpr.Compile()(); Console.WriteLine($"New expression: {newExpr.ToReadableString()}"); Console.WriteLine($"New result value: {newResult}"); // Old expression: () => Math.Sin((double)Guid.NewGuid().GetHashCode()) / 10d // Old result: 0.09489518488876232 // New expression: () => Math.Cos((double)Guid.NewGuid().GetHashCode()) / 10d // New result value: 0.07306426748550407
  • 56. Transpiling Code into a Different Language Speaker: Alexey Golub @Tyrrrz
  • 57. Speaker: Alexey Golub @Tyrrrz Expression<Action<int, int>> expr = (a, b) => Console.WriteLine("a + b = {0}", a + b)); var fsharpCode = FSharpTranspiler.Convert(expr);
  • 58. Speaker: Alexey Golub @Tyrrrz public static class FSharpTranspiler { private class Visitor : ExpressionVisitor { private readonly StringBuilder _buffer; public Visitor(StringBuilder buffer) { _buffer = buffer; } // ... } public static string Convert<T>(Expression<T> expression) { var buffer = new StringBuilder(); new Visitor(buffer).Visit(expression); return buffer.ToString(); } }
  • 59. Speaker: Alexey Golub @Tyrrrz protected override Expression VisitLambda<T>(Expression<T> node) { _buffer.Append("fun ("); _buffer.AppendJoin(", ", node.Parameters.Select(p => p.Name)); _buffer.Append(") ->"); return base.VisitLambda(node); } protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method.DeclaringType == typeof(Console) && node.Method.Name == nameof(Console.WriteLine)) { _buffer.Append("printfn "); if (node.Arguments.Count > 1) { var format = (string) ((ConstantExpression) node.Arguments[0]).Value; var formatValues = node.Arguments.Skip(1).ToArray(); _buffer.Append(""").Append(Regex.Replace(format, @"{d+}", "%O")).Append("" "); _buffer.AppendJoin(" ", formatValues.Select(v => $"({v.ToReadableString()})")); } } return base.VisitMethodCall(node); }
  • 60. Speaker: Alexey Golub @Tyrrrz var fsharpCode = FSharpTranspiler.Convert<Action<int, int>>( (a, b) => Console.WriteLine("a + b = {0}", a + b)); > let foo = fun (a, b) -> printfn "a + b = %O" (a + b) val foo : a:int * b:int -> unit > foo (3, 5) a + b = 8 val it : unit = () // fun (a, b) > printfn "a + b = %O" (a + b)
  • 61. Summary • Expression trees are fun • We can make reflection-heavy code much faster • We can do late-binding with almost no performance penalties • We can write our own runtime-compiled DSL • We can provide refactor-safe identification for type members • We can analyze specified lambdas and reflect on their structure • We can rewrite existing expressions to behave differently • We can transpile code into other languages Speaker: Alexey Golub @Tyrrrz
  • 62. Useful packages • FastExpressionCompiler https://github.com/dadhi/FastExpressionCompiler Speeds up LambdaExpression.Compile() • ReadableExpressions https://github.com/agileobjects/ReadableExpressions Converts expression to readable C# code Speaker: Alexey Golub @Tyrrrz
  • 63. Learn more • Working with expression trees in C# (by me) https://tyrrrz.me/blog/expression-trees • Introduction to expression trees (MS docs) https://docs.microsoft.com/en-us/dotnet/csharp/expression-trees • Expression trees in enterprise software (Maksim Arshinov) https://youtube.com/watch?v=J2XzsCoJM4o Speaker: Alexey Golub @Tyrrrz
  • 64. Thank you! Speaker: Alexey Golub @Tyrrrz